总结activity启动过程中View,ViewRootImpl关系

近段时间查看android的布局流程一块代码时间较多,发现频繁使用到一个类ViewRootImpl,加之曾经有仔细分析过activity启动过程,和activity的setContentView之后的显示过程,但是一直没有作为一个整体连贯起来,后来在分析布局流程中,发现诸多疑惑比如:

  1. ViewRootImpl是怎样跟布局中的所有子View关联起来的
  2. View中的mAttachInfo什么时候初始化的,作用是什么
    这里从activity的启动到调用oncreate中的setContentView再到ActivityThread中的handleResumeActivity分为三个大的流程进行分析。

1、activity的启动过程

activity的启动过程可以参见下图:
总结activity启动过程中View,ViewRootImpl关系_第1张图片

activit的启动过程及其复杂,这里只是抽取其中部分流程做讲解,暂时不对其细节做分析,比如其堆栈操作,启动模式各种参数之类的。
这里列出下回调到ActivityThread中的代码:
ActivityStackSupervisor类realStartActivityLocked方法中如下:

                // Create activity launch transaction.
                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
                        r.appToken);
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
                        profilerInfo));

                // Set desired final state.
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);

这里通过ClientTransaction添加LaunchActivityItem的回调和设置ResumeActivityItem为生命周期状态请求,先后回调了Activitythread中的handleLaunchActivity方法与handleResumeActivity方法。
TransactionExecutor.java

    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
        log("End resolving transaction");
    }
    
    public void executeCallbacks(ClientTransaction transaction) {
        ......
        item.execute(mTransactionHandler, token, mPendingActions);
        item.postExecute(mTransactionHandler, token, mPendingActions);
        ......
    }
    
    private void executeLifecycleState(ClientTransaction transaction) {
        ......
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }

LaunchActivityItem.java

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
                        PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client);
        //调用activitythread中方法
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

ResumeActivityItem.java

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
        //调用activitythread中方法
        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                "RESUME_ACTIVITY");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

到这里activity的启动流程就介绍完成,在一个activity的声明周期中,先后调用了ActivityThread的handleLaunchActivity与handleResumeActivity,在handleLaunchActivity中会回调activity的onCreate方法,handleResumeActivity中会回调activity的onResume方法。

2、activity的显示过程

在回调activity的onCreate方法中,由setContentView的调用传入布局文件,在这个过程中会完成布局文件的解析,DecorView的生成,最后在在onResume之后会将之前生成的DecorView与窗口绑定,完成所有空间的布局绘制,并且将decorview其设为可见,添加到窗口中,流程如下:
总结activity启动过程中View,ViewRootImpl关系_第2张图片

以下抽取部分细节做下讲解:

1.窗口管理服务的初始化:
窗口管理服务的初始化在activity的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, 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;
        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;

        mWindow.setColorMode(info.colorMode);

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

2.布局解析过程
在PhoneWindow的setContentView方法中,使用LayoutInflater来解析传递过来的布局文件,将布局文件中的所有控件加入到DecorView中:

    @Override
    public void setContentView(int layoutResID) {
       //检查decorview和mContentParent
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //解析布局文件
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

在LayoutInflater中解析最后会进入这样的流程:

            if (root != null && attachToRoot) {
                    root.addView(temp, params);
            }

这里的mContentParent是个FrameLayout,继承自Viewgroup:

private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {
        ......
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }
        ......

这里将布局文件中所有子view的mParent都设置为mContentParent。

3.显示过程
在activitythread的handleResumeActivity方法中,会通过WindowManager的addview方法,将前面已经初始化完成了的decorview添加到窗口中,然后完成其布局的测量、布局、绘制流程,然后将其设置为可见,这样界面就呈现在眼前了:

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String 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 (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //将decorview添加到窗口中
                    wm.addView(decor, l);
                } else {
                    ......
                }
            }
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            ......
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
       ......
    }

从前面activity中attach方法可知,这里的wm就是WindowManagerGlobal:

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
       ......

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
           ......
            int index = findViewLocked(view, false);
            ......
            //初始化ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            try {
                //关联ViewRootImpl与decorview,并且开始布局
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

ViewRootImpl中的setView方法完成了将decorview添加到窗口,进行布局,完成测量布局绘制流程等一些重要工作,点击事件接收器也是在这里初始化的,所以点击事件最先是在传递到decorview中的。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......
                //调用布局流程
                requestLayout();
                ......
                try {
                    ......
                    //添加到window
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
                   ......
                } 
                ......
                //将viewrootimpl设置为decorview的parent
                view.assignParent(this);
                ......
        }
}

requestLayout调用后会依次调用scheduleTraversals->doTraversal->performTraversals,
在performTraversals中会将viewrootimpl中初始化的mattachInfo全部赋值给子view:

    private void performTraversals() {
        ......
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        ......
    }

ViewGroup中对自己的所有子view循环一遍,就把所有的子view的mattachInfo都设置了

    @Override
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
        super.dispatchAttachedToWindow(info, visibility);
        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            final View child = children[i];
            child.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, child.getVisibility()));
        }
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        for (int i = 0; i < transientCount; ++i) {
            View view = mTransientViews.get(i);
            view.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, view.getVisibility()));
        }
    }

View中


    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        ......
    }

到这里,Decorview下的所有子view都完成了测量、布局、绘制流程,并且全部子view的mattachInfo变量也都已经设置了,ViewRootImpl中的变量mView就是DecorView,然后每个字view中的mattachInfo变量都是持有的ViewRootImpl中的实例引用,ViewRootImpl其实并不是一个View,它只是一个DecorView与窗口管理之间的一个媒介,负责界面的布局相关的管理。mAttachInfo因其持有ViewRootImpl的引用,所以它的一个重要作用就是协调view的布局重绘相关的功能。
activity中:

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

到这里界面就呈现在眼前了。

如下是整个显示过程中一些相关的类关系图:
总结activity启动过程中View,ViewRootImpl关系_第3张图片

你可能感兴趣的:(android系统源码阅读笔记)