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开发艺术探索

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