1. 对ViewRootImpl的疑问和我的见解
相信很多朋友刚学习Android的View相关的内容的时候都纠结一个问题,ViewRootImpl到底是何方神圣。我就结合下我在学习的过程中总结的谈下我对ViewRootImpl的理解吧。当然其中也会穿插一些我的疑问,欢迎大家一起讨论。
先来说下结论吧,我认为ViewRootImpl是一个概念,他不是View,他和view属于兄弟关系,他可以作为View(DecorView)的ParentView,从定义也可以看出来:
public final class ViewRootImpl implements ViewParent
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl
2. ViewRootImpl的属性
ViewRootImpl实现了接口ViewParent,那ViewParent又是什么呢?可以把View树理解成一个n叉树,那么每一个view是不是都是有一个父节点和n个子节点嘛,而Viewparent就是这么一个接口,实现了ViewParent,就说明这个view可以作为一个父节点,并切接口中声明了一些父节点需要实现的方法。
而且在View.java中我们就能找到这么一个属性, protected ViewParent mParent;
下面画了一个简易的view树如图1,便于理解。所以DecorView的结构可以看成图2(DecorView的布局有很多种,这里我们就选择了其中一个作为演示)。可以看到DecorView还有两个子View,其中一个就是mContentParent,这就让我们想到自己写的onCreate方法中的setContentView了,这个详细的我们后面再说,总之当我们去写一个程序时,调用setContentView的时候,就是在编辑这个mContentParent(这里需要声明下,mContentParent这个名称是在phoneWindow类中存在的,在DecorView类中,他的名称是mContentRoot)下面的布局。
然后再看一个我们最常见的view,FrameLayout,他的定义又是什么呢?
public class FrameLayout extends ViewGroup
看这里,FrameLayout就是继承自ViewGroup,而ViewGroup又实现了ViewParent,其实我们常见的View都是继承自View并且实现了ViewParent,所以这里就可以看到ViewRootImpl和View是不一样的东西,而ViewRootImpl之所以能作为DecorView的ParentView就是因为他实现了ViewParent,所以他是可以作为View的父节点的,因为父节点的类型只要是ViewParent就行。
3. ViewRootImpl和DecorView的关系
接下来再讲怎么成为DecorView的ViewParent吧。这里就得牵扯到WindowManagerGlobal类了。 在WindowManagerGlobal类中的addView方法中,会初始化ViewRootImpl,这里能看到root就是ViewRootImpl的实例。那DecorView的实例是谁呢?
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession, new WindowlessWindowLayout());
}
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
// BadTokenException or InvalidDisplayException, clean up.
if (viewIndex >= 0) {
removeViewLocked(viewIndex, true);
}
throw e;
}
答案就是view。能调用到这里的addView方法的,都是通过windowManager调用过来的,而查看下代码,用到WindowManagerImpl实现了WindowManager接口,所以直接看哪里调用了WindowManagerImpl的addView方法就好了。
而这里我们可以看到在handleResumeActivity方法中有这么一句,看这里addView的参数decor就是DecorView的实例,最后传到了上面的 root.setView(view, wparams, panelParentView, userId);这里的view就是decor一路传过来的。
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
再进入到setView方法看下,是怎么把root变成view的ParentView的吧。 然后再看到这里,很明显了吧,已经把view赋值给root了。
view.assignParent(this);
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
好了这次先说这么多,这里的知识点很多,比如DecorView的加载我还没说,后面会补充的。