Activity的Window对象创建过程

要分析Activity中的Window的创建过程就必须了解Activity的启动过程,详细的启动过程参考Activity的启动过程 。 Activity的启动过程很复杂, 最终会由ActivityThread中的performLaunchActivity()方法来完成整个启动过程, 在这个方法内部会通过类加载器创建Activity的实例, 并调用其attach()方法为其关联运行过程中所依赖的一系列上下文环境变量, 代码如下所示:

 /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       
      ...
       
      java.lang.ClassLoader cl = appContext.getClassLoader();
      activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

      ...
                 
      activity.attach(appContext, this, getInstrumentation(), r.token,
              r.ident, app, r.intent, r.activityInfo, title, r.parent,
              r.embeddedID, r.lastNonConfigurationInstances, config,
              r.referrer, r.voiceInteractor, window, r.configCallback);

		...

        return activity;
    }

在Activity的attach()方法里, 系统会创建Activity所属的Window对象并为其设置回调接口。 由于Activity实现了Window的Callback接口, 因此当Window接收到外界的状态改变时就会回调Activity的方法。 Callback接口中的方法很多, 但是有几个却是我们都非常熟悉的, 比如onAttachedToWindow()、 onDetachedFromWindow()、 dispatchTouchEvent(), 等等, 代码如下所示:

//Activity.java

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        
        
        ... //其他代码
        

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);

        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }

从上面的代码可以看出, Activity的Window是一个PhoneWindow对象。
到这里Window对象已经创建完成了, 下面分析Activity的视图是怎么附属在Window上的。 由于Activity的视图由setContentView()方法提供, 我们只需要看setContentView()方法的实现即可。

  /**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

从Activity的setContentView()的实现可以看出, Activity将具体实现交给了Window处理, 而Window的具体实现是PhoneWindow, 所以只需要看PhoneWindow的setContentView()即可。

//PhoneWindow.java

    @Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

PhoneWindow的setContentView()大致遵循如下几个步骤:

  1. 如果没有DecorView, 那么就创建
    DecorView是一个FrameLayout,DecorView是Activity中的顶级View, 一般来说它的内部包含标题栏和内容栏,并且内容栏具有固定的id, 那就是“content”, 它的完整id是android.R.id.content。 DecorView的创建过程由installDecor()方法来完成, 在方法内部会通过generateDecor()方法来直接创建DecorView, 这个时候DecorView还只是一个空白的FrameLayout:
//PhoneWindow.java

    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

为了初始化DecorView的结构, PhoneWindow还需要通过generateLayout()方法来加载具体的布局文件到DecorView中, 具体的布局文件和系统版本以及主题有关, 这个过程如下所示:

//PhoneWindow.java

View in = mLayoutInflater.inflate(layoutResource,null);
decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

其中ID_ANDROID_CONTENT的定义如下, 这个id所对应的ViewGroup就是mContentParent:

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content
  1. 将View添加到DecorView的mContentParent中
    这个过程就比较简单了, 由于在步骤1中已经创建并初始化了DecorView, 因此这一步直接将Activity的视图添加到DecorView的mContentParent中即可:mLayoutInflater.inflate(layoutResID, mContentParent)。
    到此为止, Activity的布局文件已经添加到DecorView里面了,

  2. 回调Activity的onContentChanged()方法通知Activity视图已经发生改变
    这个过程就更简单了, 由于Activity实现了Window的Callback接口, 这里表示Activity
    的布局文件已经被添加到DecorView的mContentParent中了, 于是需要通知Activity, 使其
    可以做相应的处理。 Activity的onContentChanged()方法是个空实现, 我们可以在子Activity
    中处理这个回调。 这个过程的代码如下所示。

//PhoneWindow.java

public void setContentView(View view, ViewGroup.LayoutParams params) {

	 ... //其他代码
	 
	 final Callback cb = getCallback();
	 if (cb != null && !isDestroyed()) {
	     cb.onContentChanged();
	 }
	        
	 ... //其他代码

}



经过了上面的三个步骤, 到这里为止DecorView已经被创建并初始化完毕, Activity的布局文件也已经成功添加到了DecorView的mContentParent中, 但是这个时候DecorView还没有被添加到WindowManager。 这里需要正确理解Window的概念,Window更多表示的是一种抽象的功能集合, 虽然说早在Activity的attach()方法中Window就已经被创建了, 但是这个时候由于DecorView并没有被添加到WindowManager, 所以这个时候的Window无法提供具体功能, 因为它还无法显示到屏幕上,也无法接收外界的输入信息。 在ActivityThread的handleResumeActivity()方法中, 首先会调用Activity的onResume()方法, 接着会调用Activity的makeVisible(), 正是在makeVisible()方法中, DecorView真正地完成了添加和显示这两个过程, 到这里Activity的视图才能被用户看到, 如下所示。

//Activity.java

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes()); //DecorView添加到WindowManager
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE); //设置DecorView的显示View.VISIBLE
    }

到这里, Activity中的Window的创建过程已经分析完了。

参考:
《Android开发艺术探索》

https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/base/core/java/com/android/internal/policy/DecorView.java

发布了535 篇原创文章 · 获赞 94 · 访问量 74万+

猜你喜欢

转载自blog.csdn.net/yzpbright/article/details/105006061