DecorView添加到Window过程的源码分析

前言

上一篇我们分析了Activity的setContentView()AppCompatActivity的setContentView()执行过程,只是将Activity显示的视图加载到了DecorView中,DecorView还没有和Window进行关联,下面我们详细分析一下

ActivityThread.java

DecorView添加到Window是在Activity的启动过程中完成的,Activity的启动过程最终会执行handleLaunchActivity()方法,在这个方法里先后执行以下方法,我们依次分析

  • WindowManagerGlobal.initialize(); // Initialize before creating the activity`
  • Activity a = performLaunchActivity(r, customIntent);
  • handleResumeActivity()

首先在Activity创建之前WindowManager会获取到WindowManagerService(wms)进行一系列的初始化,在这里我们先不分析wms(咳咳。。其实我还没有深入分析呢!),我们继续看下performLaunchActivity(),

ActivityThread.java中performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {   //获得包的信息
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        ComponentName component = r.intent.getComponent();
        if (component == null) {        
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());   //获得PMS
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        ...
      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之前会获得包的信息,然后通过类加载起来实例化我的要创建的Activity,然后执行了attach()方法,继续撸。。。

Activity.java中的attach()

   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) {
        ...
        mWindow = new PhoneWindow(this, window);     //创建PhoneWindow()
        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;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

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

在Activity的attach()方法中,首先初始化我们的PhoneWindow,PhoneWindow是Window的唯一实现类,然后进行一系列的赋值,也就是说初始化的过程是在attach()中发起调用的,初始化attach()完成之后,我们再回到ActivityThread的performLaunchActivity()方法,还没有分析完

ActivityThread.java中performLaunchActivity()

               activity.attach(); //初始化完成
               activity.mCalled = false;
                if (r.isPersistable()) {    //isPersistable先前持久化的状态(或null)
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
              ...
            }
            r.paused = true;

            mActivities.put(r.token, r);

在这里我们看到了熟悉的字眼ActivityOnCreate(),没错就是在这个方法里我们的Activity的生命周期开始执行了,再往下调用了callActivityOnRestoreInstanceState(),也就是我们现场状态的还原,ActivityThread中的performLaunchActivity()执行完后我们再看下handleResumeActivity()

ActivityThread.java中handleResumeActivity()

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        ...
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);
        ...
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
              ...  
    }

这个方法主要都做了哪些事情呢,首先执行performResumeActivity(),也就是里面会执行Activity的OnResume(),然后获得Window,也就是PhoneWindow,在获得DecorView和WindowManager,我们发现WindowManager是一个接口继承ViewManager,我们又要找到它的实现类,它的实现类是

public interface WindowManager extends ViewManager 
public final class WindowManagerImpl implements WindowManager

handleResumeActivity()中调用addView(decor,l)将DecorView作为参数传进来,看下addView()

     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

我们发现实际是获得WindowManagerGlobal的实例,然后调用的是WindowManagerGlobal的addView()

WindowManagerGlobal.java的addView()


    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ...
        ViewRootImpl root;
        View panelParentView = null;
        ...
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);
        ...

我们会看到ViewRootImpl,这个类相信大家平时都会看到,这个类主要是做哪些事情呢,其实我们UI的绘制流程都是在这个类里完成的,UI的绘制我们先不分析,接着看上面代码,会创建一个ViewRootImpl,然后将view也就是传过来的DecorView,和创建的ViewRootImpl实例root还有对应的mParams保存到
WindowManagerGlobal中,也就是说我们的WindowManagerGlobal是来管理ViewRootImpl和DecorView的,再继续会调用ViewRootImpl的setView()的方法,并且将DecorView和params传过去,关键的地方

ViewRootImpl.java的setView()

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            if (mView == null) {
                mView = view;
               ...
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                ...
                addToDisplay();
               ...
               view.assignParent(this);

    }
  • requestLayout()从方法名字可以知道,“请求布局”,那就是说,如果调用了这个方法,那么对于一个子View来说,应该会重新进行布局流程。但是,真实情况略有不同,如果子View调用了这个方法,其实会从View树重新进行一次测量、布局、绘制这三个流程,最终就会显示子View的最终情况。
  • addToDisplay();其实这个方法里就是采用aidl将DecorView添加显示出来啦。看到这里我们是不是很清晰Activity启动流程过程中怎么样把我们这个PhoneWindow生成出来,把我的DecorView显示出来到UI绘制有了一定的了解了。

我们再看下requestLayout()里面都做了哪些事

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

requestLayout()中调用了scheduleTraversals();方法,scheduleTraversals();代码如下

    void scheduleTraversals() {
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }

这里启动一个 TraversalRunnable,

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

在一个新的线程中有执行doTraversal()方法,doTraversal()方法

    void doTraversal() {
            ...
            performTraversals();
            ...
        }
    }

doTraversal()里有执行performTraversals(),这个performTraversals()里面就进行的我们UI的绘制,也就是说performTraversals()就是UI绘制的入口,

private void performTraversals() {
        // Ask host how big it wants to be
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        performLayout(lp, mWidth, mHeight);
        performDraw();
}

下面看下流程图


DecorView添加至窗口的过程.png

总结

至此,我们已经掌握了DecorView添加到Window的过程,首先在Activity启动的过程中,执行performLaunchActivity(),这里面在Activity的attach()中创建了PhoneWindow,在handleResumeActivity()过程中拿到WindowManager和DecorView,然后创建一个WindowManagerGlobal的addView()方法中创建ViewRootImpl并对ViewRootImpl和DecorView进行管理,最后调用ViewRootImpl的setView()来添加显示,并且找到了UI绘制的起始点。下一篇我们来分析UI的绘制流程

推荐

Activity的setContentView()源码分析
AppCompatActivity的setContentView()源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容