从源码揭示PhoneWindow,ViewRoot,ViewManager与DecorView之间的关系

上一篇文章由setContentView()方法引发的思考看到了setContentView()对安卓源码的一些思考,也揭示了Window,PhoneWindow,DecorView他们几个的关系。但咱们从上篇文章里留下了一些疑问WindowManager,ViewRoot,ViewRootImpl,PhoneWindow,WindowManagerService他们到底是什么,他们之间有什么样的关系呢?看了网上不少文章,终于对他们有所了解,下面我就从Activity启动一步一步分析到底咱们看到的View是怎么建立出来的。 关于Activity的启动流程至关复杂,我还没时间去深究,可是从网上资料看出,Activity的启动最后会到ActivityThread类的handleLaunchActivity方法里面,那好,咱们就去相关的类看看:java

...
 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
            ...
    }
}
复制代码

一来就是就有WindowManagerGlobal这个没见过的对象,但从名字来看应该跟WindowManager有必定的关系。这里先无论,往下看performLaunchActivity(r, customIntent)方法:bash

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try { //Activity经过ClassLoader建立出来
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);  
        } ...
        try {
            //建立Application
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
            if (activity != null) {
                //建立Activity所需的Context
                Context appContext = createBaseContextForActivity(r, activity);
                ...
                //将Context与Activity进行绑定
                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);
                ...
                    //调用activity.oncreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                ...
                     //调用Activity的onstart方法
                     activity.performStart();
                    //调用activitu的OnRestoreInstanceState方法进行Window数据恢复 
                     mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
                ...
        return activity;
    }

复制代码

咱们先看到mInstrumentation.newActivity(cl,component.getClassName(), r.intent)方法session

public Activity newActivity(ClassLoader cl, String className,Intent intent)throws InstantiationException,IllegalAccessException,ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
}
复制代码

是经过ClassLoader把activity建立出来,接着经过makeApplication(false, mInstrumentation)方法建立Application,接下来咱们看看咱们熟悉的attach()方法:app

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) {
        //ContextImpl的绑定
        attachBaseContext(context);
        //在当前Activity建立Window
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...
        //为Window设置WindowManager
        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());
        }
        //建立完后经过getWindowManager就能够获得WindowManager实例
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
复制代码

咱们能够看到一个咱们想要看到的东西出来了PhoneWindow和WindowManager,首先这里建立了一个PhoneWindow对象,而后经过setWindowManager建立出一个WindowManager对象,而后将他们绑定在一块儿。而后咱们点进去看看WindowManager:异步

忽然感受到又撞板了,WindowManager只是一个接口,并非具体的实体类。那咱们只好看看setWindowManager方法里面究竟怎么操做的:

mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
        || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
    wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
复制代码

好吧,这下放心了,WindowManagerImpl就是WindowManager的实现类。 接下来会到performLaunchActivity():ide

mInstrumentation.callActivityOnCreate(activity, r.state ...);
复制代码

该方法则是调用activity.oncreate方法的布局

public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}
    
final void performCreate(Bundle icicle) {
    onCreate(icicle);
    mActivityTransitionState.readState(icicle);
    performCreateCommon();
}
复制代码

接下来就是onstart()post

activity.performStart();
复制代码

接下来是咱们熟悉的:ui

mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
复制代码
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,PersistableBundle persistentState) {
    activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
复制代码

里面经过Bundle来保存恢复Window窗口信息的,接下来回到handleLaunchActivity()方法:this

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...
        //上面分析完了
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
            ...
    }
}
复制代码

奇怪,怎么过了onStart()方法都尚未出现View的绘制的三大流程呢,按我之前的理解。别着急,好戏在后头,咱们看看handleResumeActivity()方法的实现吧:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ...
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;
            ...
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                //获取DecorView
                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) {
                    ...
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把当前的DecorView与WindowManager绑定一块儿
                    wm.addView(decor, l);
                }
            } 
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    //而后调用这个方法回调,表示屏幕参数发生了改变
                    performConfigurationChangedForActivity(r, r.newConfig);
                    ...
                    r.newConfig = null;
                }

                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                 //因为前面设置了INVASIBLE,因此如今要把DecorView显示出来了
                    r.activity.makeVisible();
                }
            }

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                   //通知ActivityManagerService,Activity完成Resumed ActivityManager.getService().activityResumed(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }

        } 
    }
复制代码

handleResumeActivity方法一开始就调用了activity = performResumeActivity()方法,但看过里面的实现,并无咱们想要的东西,因此代码就不贴出来的。重点在下面,

(1)经过r.window.getDecorView()方法获取了DecorView;

(2)而后把DecorView设置为INVISIBLE;

(3)而后获取ViewManager

(4)经过addView的方法把DecorView绑定到ViewManager上;

(5)而后调用这个方法回调,表示屏幕参数发生了改变;

(6)经过makeVisible()的方法,把以前INVISIBLE的DecorView显示出来;

(7)通知ActivityManagerService,Activity完成Resumed。

咱们看到handleResumeActivity()方法作了不少事,咱们最关注的是第(4)点。看到这个,忽然间灵光一现,记起来上篇文章摘录艺术探索的时候有一句话:

ViewRoot对应于ViewRootImpl类,它是链接WindowManager和DecorView的纽带

到这里咱们都没看到ViewRoot的身影,只在上面看到了ViewRootImpl,既然他们之间有什么关系,那么咱们就看看wm.addView(decor, l)这个方法里面弄个明白吧。

好吧,点下去是一个ViewManager,ViewManager有addView(),updateViewLayout(),removeView()三个方法,并且咱们知道WindowManager是继承ViewManger的接口,并且WindowManager的WindowManagerImpl实现类是WindowManagerImpl,因此下面咱们去看看WindowManagerImpl的addView是怎么实现的:

mGlobal对象是否是有点熟悉,没错,他就是文章开头写的WindowManagerGlobal类。咱们看看WindowManagerImpl类,发现addView(),updateViewLayout(),removeView()三个方法的实现都不是他本身完成的,而是交由WindowManagerGlobal去实现。那么接下来咱们就看看WindowManagerGlobal的addView()方法又是怎么样的:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
        ViewRootImpl root;
        View panelParentView = null;
        
        ...
            
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        //ViewRootImpl开始绘制view
        root.setView(view, wparams, panelParentView);
        ...
    }
复制代码

噢,咱们终于见到ViewRootImpl的身影了,咱们查资料发现,原来ViewRoot其实就是ViewRoot,那么咱们总算找到咱们想要的东西了。咱们看看root.setView()方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ...
                // 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();
                ...
                try {
                ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
    }
复制代码

在setView方法中, 首先会调用到requestLayout(),表示添加Window以前先完成第一次layout布局过程。requestLayout最终会调用performTraversals方法来完成View的绘制。

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

咱们看看checkThread()方法:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
复制代码

这个方法实际上是判断你是否在子线程更新UI线程的,因此说当你在onCreate()方法:

大家以为会报错吗,答案是不会的,由于判断你是否在子线程更新UI线程是在onResume()方法里面判断的。

接着咱们往下看scheduleTraversals()方法:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        ...
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
      ...
    }
}
复制代码

scheduleTraversals中会经过handler去异步调用mTraversalRunnable接口

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
复制代码
void doTraversal() {
            ...
            performTraversals();
            ...
    }
复制代码

接着看performTraversals()方法:

private void performTraversals() {  
    ......  
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ......  
    performDraw();
    }
    ......  
} 
复制代码

看到这里你们应该都懂了,绘制的三大流程(measure,layout,draw)就是在这里。那咱们得出的结果是:View的绘制是ViewRootImpl完成的,并且WindowManager的addView()方法实际上最终的实现也在ViewRootImpl里面。

ViewRoot对应于ViewRootImpl类,它是链接WindowManager和DecorView的纽带。

那这句话的意思你们也终于明白了吧。

接着咱们返回setView()方法,addToDisplay()方法会经过WindowSession最终来完成Window的添加过程。在下面的代码中mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说这实际上是一次IPC过程,远程调用了Session中的addToDisPlay方法。

@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
复制代码

这里的mService就是WindowManagerService,也就是说Window的添加请求,最终是经过WindowManagerService来添加的。这里咱们终于见到WindowManagerService了。

然而还有一个问题,为何咱们在开发的时候,在onCreate方法中调用view.getMeasureHeight() = 0呢?缘由很简单,那是由于activity绘制的三大流程发生在activity.handleResumeActivity()方法中。那有人继续问,那在onResume()方法调用为何view.getMeasureHeight()仍是会等于0,缘由也很简单,由于ViewRootImpl绘制View是异步进行的,因此为了让咱们拿到这个测量结果的数值,在viewRootImpl的performTraversals的测量布局以后添加了:

if (triggerGlobalLayoutListener) {
    mAttachInfo.mRecomputeGlobalAttributes = false;
    mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
...
performDraw();
复制代码

也就是咱们日常用的:

view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
    // TODO Auto-generated method stub
             
    }
});
复制代码

利用观察者模式在onCreate()方法里面设置监听,咱们就能就能获得想要的测量结果了。

而View是怎么跟ViewRootImpl绑定的呢?

这篇文章讲到很详细: Android窗口机制(四)ViewRootImpl与View和WindowManager

在ViewRootImpl的构造方法中:

public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();
    ...
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
    ...
}
复制代码
AttachInfo(IWindowSession session, IWindow window, Display display,ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
    mSession = session;
    mWindow = window;
    mWindowToken = window.asBinder();
    mDisplay = display;
    mViewRootImpl = viewRootImpl;
    mHandler = handler;
    mRootCallbacks = effectPlayer;
}

复制代码

而ViewRootImpl是在WindowManagerGlobal的addView()中建立的:

root = new ViewRootImpl(view.getContext(), display);
复制代码

这就很清楚了吧。下面小弟画了个图总结了一下,若是有错的话请你们来纠正一下,谢谢~

参考文献:

《Android开发艺术探索》

Android窗口机制(四)ViewRootImpl与View和WindowManager

个人掘金: juejin.im/user/594e8e…

个人简书: www.jianshu.com/u/b538ca57f…

相关文章
相关标签/搜索