ViewRootImpl是一个及其重要的类,主要作用如下:
(1)将DecorView传递给WindowManagerSerive。
(2)完成View的绘制过程,包括measure、layout、draw过程。
(3)向DecorView分发收到的用户发起的event事件,如按键,触屏等事件。
此外,ViewRootImpl中还包含了两个需要重点关注的内部类:
(1)final class ViewRootHandler extends Handler
用于向DecorView分发事件
(2)static class W extends IWindow.Stub
W是ViewRootImpl的一个嵌入类,也是一个Binder服务。通过mWindowsession.addToDisplay函数传入WMS,用来在WMS中通过Binder回调。
/** * The top of a view hierarchy, implementing the needed PRotocol between View * and the WindowManager. This is for the most part an internal implementation * detail of {@link WindowManagerGlobal}. * * {@hide} */ 通过这一段注释,我们知道,ViewRootImpl他是View树的树根,但它却又不是View。ViewRootImpl的创建在ActivityThread中,调用栈如下: ActivityThread#handleResumeActivity()->WindowManagerImpl#addView()->WindowManagerGlobal#addView()->ViewRootImpl(view.getContext(),Display)。 1.1 构造函数选取ViewRootImpl构造函数的一部分源码如下:mWindowSession = WindowManagerGlobal.getWindowSession(); mWindow = new W(this); mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); 第一句mWindowSession赋值为WindowManagerGlobal.getWindowSession(),其返回值为一个Session对象,Session的实例化在WMS进程中进行的,ViewRootImpl中有一个Session的代理对象,所以可以通过Session主要调用WMS中一些方法。 第二、三句中的W继承于IWindow.Stub,后者继承于Binder又实现了IWindow接口,因此这个W是可以ipC的。第三句中将mWindow赋值给了View.AttachInfo中的mWindow对象,将mWindowSession赋值给了mSession变量。1.2 ViewRootImpl#setViewpublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; //略 requestLayout(); //略 try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } //略 } } } 首先,在setView内部会通过requestLayout来完成异步刷新请求,requestLayout最终会调用performTraversals方法来完成View的绘制。 接着,会通过WindowSession最终来完成Window的添加过程。上面的代码中,mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。 由此可知,接下去Window的添加请求就交给WindowManagerService去处理了。addView大概一个过程如下:WindowManager——>WindowManagerGobal——>ViewRootImpl——>Session——>WindowManagerService2、完成View的绘制过程
整个View树的绘图流程是在ViewRootImpl类的performTraversals()方法中进行的,该函数做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小(measure)、是否重新放置视图的位置(layout)、以及是否重绘 (draw),其核心也就是通过判断来选择顺序执行这三个方法中的哪个,如下:private void performTraversals() { ...... //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来 //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ...... mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... } performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的。 另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。其中requestLayout()方法会调用measure过程和layout过程,不会调用draw过程,也不会重新绘制任何View包括该调用者本身。3、向DecorView分发事件
这里的事件不仅仅包括MotionEvent,还有KeyEvent。我们知道View的时间分发顺序为Activity——>Window——>View,那么Activity的事件来源在哪里呢?这是个需要思考的问题,答案和ViewRootImpl有很大的关系。 首先,事件的根本来源来自于硬件,经过native层,然后会经过InputEventReceiver接受事件,然后交给ViewRootImpl,将事件传递给DecorView,DecorView再交给PhoneWindow,PhoneWindow再交给Activity。这样看来,整个体系的事件分发顺序为:3.1 ViewRootImpl#dispatchInputEventpublic void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { SomeArgs args = SomeArgs.obtain(); args.arg1 = event; args.arg2 = receiver; Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args); msg.setAsynchronous(true); mHandler.sendMessage(msg); } InputEvent输入事件,它有2个子类:KeyEvent和MotionEvent,其中KeyEvent表示键盘事件,而MotionEvent表示点击事件,这里InputEventReceiver译为输入事件接收者,顾名思义,就是用于接收输入事件,然后交给ViewRootImpl的dispatchInputEvent方法去分发处理。可以看到mHandler将逻辑切换到UI线程,代码如下。final ViewRootHandler mHandler = new ViewRootHandler(); final class ViewRootHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { ........ { SomeArgs args = (SomeArgs)msg.obj; InputEvent event = (InputEvent)args.arg1; InputEventReceiver receiver = (InputEventReceiver)args.arg2; enqueueInputEvent(event, receiver, 0, true); args.recycle(); } break; ................. } (未完待续)
新闻热点
疑难解答