Window的創建過程

1.activity的window創建

activity的window創建過程跟activity的啓動過程息息相關,activity啓動過程中最終會由ActivityThread中的performLaunchActivity()方法來完成整個啓動,在該方法內部會通過類加載器加載創建activity的實例對象,並且通過attach方法爲activity關聯運行過程中所依賴的一些列上下文環境。

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      ...
             java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);


    ...
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);

                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);


        return activity;
    }

在activity的attach方法中系統會創建activity所屬的window對象,併爲其設置回調接口,如下

        //6.0以上源碼
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
//5.0源碼
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);

可以看到不同版本的SDK源碼在window創建這裏是有點不一樣,5.0以下的代碼通PolicyManager.makeNewWindow方法來創建window對象,PolicyManager是一個策略類,它的真正實現是Policy類,該類中的makeNewWindow方法實現如下:


public Window  makeNewWindow(Context context){
   return new PhoneWindow(context);
}

從5.0版本源碼可以看出最終也是new了一個PhoneWindow對象,而6.0的源碼直接mWindow = new PhoneWindow(this, window);,其實兩者本質是一樣的,。到這裏,window已經創建完了,下面分析activity的視圖是如何顯示在window上的。而 Activity 的視圖是由 setContentView 提供,所以從 setContentView 入手,它的源碼如下:


public void setContentView(int layoutResID){
   getWindow().setContentView(layoutResID);
   initWindowDecorActionBar();
}

從該方法可以看出,activity將具體的實現交給了window處理,而window的實現類是phonewindow,因此看phonewindow中的setContentView方法即可;

 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.
        //1.如果沒有DecorView  就創建它
        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 {
        //將2.view添加到DecorView的mContentParent
        //6.0的寫法
            mContentParent.addView(view, params);
        //5.0的寫法:
            //mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
        //3.回調onContentChanged方法通知activity視圖已經發生改變
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

該方法有下面幾個步驟:

1.如果沒有DecorView 就創建它

DecorView 是activity中的頂級view,它的創建過程由installDecor方法來完成,該方法內部會通過generateDecor方法來直接創建DecorView ,這個時候DecorView 還是一個空白的FrameLayout, 接着,爲了初始化DecorView 的結構,在installDecor方法內部還會調用generateLayout方法來加載具體的佈局文件到DecorView 。

2.將view添加到DecorView的mContentParent

這一步直接將activity的視圖添加到DecorView的mContentParent即可,注意5.0以前的源碼跟以後源碼的差異,不過本質都是相同的。

3.回調Activity的onContentChanged方法通知Activity視圖已經發生改變

經過前面三個步驟,DecorView 已經被創建並且初始化完畢,Activity的佈局文件也已經成功添加到了DecorView 的mContentParent中,但是這時DecorView 還沒有沒被WindowManager正式添加到window中。雖然在Activity的attach方法中window就已經被創建了,但是這個時候由於DecorView 並沒有被WindowManager識別,所以這個時候的window無法提供具體功能,還無法接收外界的輸入信息。在ActivityThread中的handleResumeActivity方法中,首先會調用activity的onResume方法,接着會調用activity的makeVisible方法,在該方法中,DecorView真正完成了添加和顯示的過程,此時activity的視圖才能被用戶看到,

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

2.Dialog的Window創建

1.創建Window

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (themeResId == 0) {
                final TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                themeResId = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, themeResId);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        //6.0
        final Window w = new PhoneWindow(mContext);
        //5.0  Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }

Dialog中window的創建在5.0中通過PolicyManager.makeNewWindow(mContext)來創建,創建後的對象實際就是PhoneWindow,6.0以後的源碼則直接創建了PhoneWindow對象。

2.初始化DecorView 並將Dialog視圖添加到DecorView

 public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

和activity的過程類似,也是通過window去添加

3.將DecorView 添加到Window中並顯示出來

這個過程在Dialog的show方法中有體現

        mWindowManager.addView(mDecor, l);
        mShowing = true;

經過上面步驟Dialog的window創建完畢,它跟activity的window創建過程很類似。當dialog消失時,在dismiss方法中會通過 mWindowManager.removeViewImmediate(mDecor); 這句代碼來移除DecorView
注意:普通的Dialog來創建時必須採用Activity的Context,如果採用Application的Context會報錯,系統Window除外
參考:Android開發藝術探索

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章