首页 > 学院 > 开发设计 > 正文

ViewRootImpl解析

2019-11-09 19:07:08
字体:
来源:转载
供稿:网友

        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回调。

1、将DecorView传递给WMS

在ViewRootImpl.java中,开始的注释如下:
/**  * 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#setView
public 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——>WindowManagerService

2、完成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#dispatchInputEvent
public 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;          .................     }  (未完待续)
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表