-
View的层级结构
-
ActivityThread的performLaunchActivity方法
我们知道,在Activity的创建流程中,在ActivityThread的performLaunchActivity方法中调用了Activity的attach方法,在attach方法里创建了PhoneWindow,然后调用setWindowManager给PhoneWindow设置了WindowManager,这是我们需要理解这一切的前提。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { ... if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); } public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }
通过以上,我们记住PhoneWindow的mWindowManager是WindowManagerImpl。
-
Activity的setContentView方法
在performLaunchActivity方法的最后会回调Activity的onCreate方法,我们知道,在onCreate流程中会创建出PhoneWindow的DecorView:
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { //DecorView的创建 installDecor(); } ... if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { ... } else { //自定义xml布局的View加载 mLayoutInflater.inflate(layoutResID, mContentParent); } ... }
-
ActivityThread的handleResumeActivity方法
performLaunchActivity流程之后接着会调用handleResumeActivity流程:
@Override public void handleResumeActivity(IBinder token, ...) { ... 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; ... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else ... } } else if ... ... }
ViewManager是WindowManager实现的接口,可以看到,这里调用了WindowManager,也就是WindowManagerImpl的addView方法,第一个参数就是要添加的View,也就是DecorView:
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { ... mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
mGlobal是WindowManagerGlobal:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; } }
可见,Android中所有的界面Window的DecorView都是通过同一个WindowManagerGlobal添加的。
-
WindowManagerGlobal的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; //单例且有异步需求,因此这里需要同步 synchronized (mLock) { ... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
我们或许在学习过程中都听过ViewRootImpl的名字,原来它是在这里创建的,它的setView方法如下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... mAttachInfo.mRootView = view; ... mAdded = true; ... requestLayout(); ... view.assignParent(this); ... } } }
可以看到这里调用了requestLayout方法,对于这个方法我们应该有些眼熟,正是这个方法开启了绘制流程,我们下面会讲到。
assignParent方法的意义是什么呢?
void assignParent(ViewParent parent) { if (mParent == null) { mParent = parent; } else if (parent == null) { mParent = null; } else { throw new RuntimeException("view " + this + " being added, but" + " it already has a parent"); } }
它给View的mParent方法赋值为ViewRootImpl本身,这是为了在View中调用requestLayout方法时可以回调到ViewRootImpl中(我们有时会通过手动调用requestLayout来触发界面的刷新和变化,像setLayoutParams这一类API内部也会调用requestLayout方法来刷新绘制):
//View中的requestLayout方法 public void requestLayout() { ... if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } ... }
可见,这里会调用mParent,也就是ViewRootImpl的requestLayout方法,这会触发自上而下的绘制流程。
-
总结
至此,我们总结一下View的层级结构:
- 所有的Window(即Activity和Dialog中的PhoneWindow)都有一个ViewRootImpl,这些ViewRootImpl会被放到WindowManagerGlobal的mRoots(一个ArrayList)中保存,WindowManagerGlobal是单例,负责管理所有的ViewRootImpl。
- ViewRootImpl中的mView就是DecorView。
- DecorView中的mContenParent用来盛放我们xml布局中解析加载出的所有View(不在本篇讨论)。
通过上面的分析,我们知道了对于一个Activity来说,它内部持有了一个PhoneWindow,PhoneWindow内部又持有一个DecorView,而在handleResumeActivity流程中,WindowManagerImpl会创建ViewRootImpl,然后把DecorView赋值到它的mView上,DecorView是一个FrameLayout,自定义的xml布局中的根View会被添加到它的mContentParent中,这就是整个View层的结构。
-
-
ViewRootImpl的requestLayout方法
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
调用scheduleTraversals方法:
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } }
Choreographer的postCallback方逻辑中不管是走哪一路最终都会走到scheduleVsyncLocked中:
private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); }
mDisplayEventReceiver是FrameDisplayEventReceiver实例,scheduleVsync方法的实现在FrameDisplayEventReceiver的父类DisplayEventReceiver中实现:
public void scheduleVsync() { ... nativeScheduleVsync(mReceiverPtr); ... }
nativeScheduleVsync会绑定屏幕刷新信号,在刷新信号到来时,会执行FrameDisplayEventReceiver的run方法,其中会调用doFrame方法,在doFrame方法中会调用:
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks方法中会按照Choreographer.CALLBACK_TRAVERSAL查找mCallbackQueues中的CallbackRecord然后执行它的run方法,CallbackRecord是在前面mChoreographer.postCallback传入的,Choreographer.CALLBACK_TRAVERSAL对应的也就是ViewRootImpl中的mTraversalRunnable:
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
可见最终调用的是doTraversal方法,它内部会调用performTraversals方法:
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; ... dispatchApplyInsets(host); ... performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... performLayout(lp, mWidth, mHeight); ... performDraw(); ... }
performMeasure方法中:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
可以看到从这里进入到DecorView的measure流程,performLayout和performDraw也是同理的逻辑,也就是说三大绘制流程就是在这里依次触发的。