Android中的ViewRootImpl类源码解析
来源:互联网 发布:合金弹头 知乎 编辑:程序博客网 时间:2024/06/11 10:49
ViewRoot目前这个类已经没有了,是老版本中的一个类,在Android2.2以后用ViewRootImpl代替ViewRoot,对应于ViewRootImpl.Java,他是链接WindowManager和DecorView的纽带,另外View的绘制也是通过ViewRootImpl来完成的。
它的主要作用我的总结为如下:
A:链接WindowManager和DecorView的纽带,更广一点可以说是Window和View之间的纽带。
B:完成View的绘制过程,包括measure、layout、draw过程。
C:向DecorView分发收到的用户发起的event事件,如按键,触屏等事件。
注:如果分析不对的地方,欢迎批评指正。
一、链接WindowManager和DecorView。
首先说第一个主要作用,链接WindowManager和DecorView,在ViewRootImpl.java中,开始的注释如下:
通过这一段注释,我们知道,ViewRootImpl他是View树的树根,但它却又不是View,实现了View与WindowManager之间的通信协议,具体的实现详情在WindowManagerGlobal这个类中。
那么View与WindowManager之间是怎么建立联系的呢,WindowManager所提供的功能很简单,常用的只有三个方法,即添加View,更新View和删除View,当然还有其它功能哈,比如改变Window的位置,WindowManager操作Window的过程更像是在操作Window中的View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager。
Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window又通过ViewRootImpl与View建立联系,因此Window并不是实际存在的,他是以View的形式存在的。这点从WindowManager的定义也可以看出,它提供的三个接口方法addView,updateView,removeView都是针对View的,这说明View才是Window的实体,在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。而对Window的访问(添加,更新,删除)都是通过ViewRootImpl实现的。这里以Window的添加过程为例,删除过程,更新过程就不再赘述了。
Window的添加过程
Window的添加过程需要通过WindowManager的addView来实现,WindowManager又是一个接口,它的实现类是WindowManagerImpl,在WindowManagerImpl中的三大操作如下:
可以看出,WindowManagerImpl又调用了WindowManagerGloble的三大操作方法,这正好说明了ViewRootImpl类上面一开始那个注释了。This is for the most part an internal implementation detail of {@link WindowManagerGlobal}.
addView方法源码如下:
addView方法主要分为如下几步:
1、检查参数是否合法
2、创建ViewRootImpl并将View添加到列表中
在WindowManagerGlobal内部有如下几个列表比较重要:
在上面的声明中吗,mViews存储的是所有Window所对应的View,mRoots存储的是所有Window所对应的ViewRootImpl,mParams存储的是所有Window所对应的布局参数,而mDyingViews存储了那些正在被删除的View对象,或者说是那些已经调用removeView方法但是还没有删除的Window对象。在addView方法中通过如下方式将Window的一系列对象添加到列表中。
3、通过ViewRootImpl来更新界面并完成Window的添加过程
这个步骤由ViewRootImpl的setView方法来完成,
在setView内部会通过requestLayout来完成异步刷新请求,requestLayout最终会调用performTraversals方法来完成View的绘制,源码注释如下:差不多意思就是在添加Window之前先完成第一次layout布局过程,以确保在收到任何系统事件后面重新布局。
接着会通过WindowSession最终来完成Window的添加过程。在下面的代码中mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。
Session中的addToDisPlay方法如下:Session这个类在package com.android.server.wm
可以看出,Window的添加请求就交给WindowManagerService去处理了。addView大概一个过程如下:
WindowManager——>WindowManagerGobal——>ViewRootImpl——>Session——>WindowManagerService
那么WindowManager又是如何与DecorView相连的呢,最终DecorView肯定是要添加到Window上的,而Window的具体实现类是PhoneWindow,因为DecorView嵌入在Window上,如图所示:
在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,可以参考一下代码,在ActvityThread中,也就是ViewRootImpl是DecorView的父元素,但是ViewRootImpl并不是View。
我们平时在Activity调用setContentView会调用PhoneWindow的setContentView,最后会调用DecorView的addView方法,这也说明了我们添加的View是DecorView的子元素。
二、完成View的绘制过程
整个View树的绘图流程是在ViewRootImpl类的performTraversals()方法(这个方法巨长)开始的,该函数做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小(measure)、是否重新放置视图的位置(layout)、以及是否重绘 (draw),其核心也就是通过判断来选择顺序执行这三个方法中的哪个,如下:
performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的,另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。其中requestLayout()方法会调用measure过程和layout过程,不会调用draw过程,也不会重新绘制任何View包括该调用者本身。
三、向DecorView分发事件。
这里的事件不仅仅包括MotionEvent,还有KeyEvent。我们知道View的时间分发顺序为Activity——>Window——>View,那么Activity的事件来源在哪里呢?这是个需要思考的问题,答案和ViewRootImpl有很大的关系。
首先,事件的根本来源来自于Native层的嵌入式硬件,然后会经过InputEventReceiver接受事件,然后交给ViewRootImpl,将事件传递给DecorView,DecorView再交给PhoneWindow,PhoneWindow再交给Activity。这样看来,整个体系的事件分发顺序为:
那么这一过程又是怎么实现的呢?
首先看ViewRootImpl的dispatchInputEvent方法。
InputEvent输入事件,它有2个子类:KeyEvent和MotionEvent,其中KeyEvent表示键盘事件,而MotionEvent表示点击事件,这里InputEventReceiver译为输入事件接收者,顾名思义,就是用于接收输入事件,然后交给ViewRootImpl的dispatchInputEvent方法去分发处理。可以看到mHandler将逻辑切换到UI线程,代码如下。
{在mHandler的UI线程中,最终调用了enqueueInputEvent方法,该方法就是将输入事件打包,利用InputEvent,InputEventReceiver构造对象QueueInputEvent,然后加入到待处理的事件队列中,代码如下:
enqueueInputEvent方法又会调用doProcessInputEvents方法或者scheduleProcessInputEvents方法,这其实是同步或者同步处理消息队列的,同步或者异步根据传入的标志位processImmediately来判断。scheduleProcessInputEvents方法只是利用mHandler向UI线程发送了一个message,代码如下:
UI线程处理的代码为:
这下就很清楚了,即使是调用了scheduleProcessInputEvents方法,最终还是会调用doProcessInputEvents方法,只是同步与异步的区别。doProcessInputEvents的代码如下:
代码中的注释已经写得很清楚了,就是逐个分发输入事件队列中的事件,分发一个便从队列中删除,其实就是单链表的操作,分发过程过程用过deliverInputEvent方法,当队列中的输入事件都已经处理完,就立即清除标志位。deliverInputEvent方法如下:
在ViewRootImpl中,有一系列类似于InputStage(输入事件舞台)的概念,他是一个抽象类,它的deliver方法会处理一个输入事件。处理完成之后会调用finishInputEvent方法。
它有很多子类,对应具体的InputStage,每种InputStage可以处理一定的事件类型,比如AsyncInputStage、SyntheticInputStage、NativePostImeInputStage、ViewPreImeInputStage、ViewPostImeInputStage等,它的子类实现了InputStage的一些抽象方法,比如onProcess、onDeliverToNext、processKeyEvent、processPointerEvent、processTrackballEvent、processGenericMotionEvent,从这些方法大概可以看出意思,在不同的情况下,onProcess、onDeliverToNext方法就会被回调。
当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理。对于点击事件来说,ViewPostImeInputStage可以处理它,ViewPostImeInputStage中,ViewPostImeInputStage类中的onProcess方法如下。当onProcess被回调时,processKeyEvent、processPointerEvent、processTrackballEvent、processGenericMotionEvent至少有一个方法就会被调用,这些方法都是属于ViewPostImeInputStage的。
在processKeyEvent、processPointerEvent、processTrackballEvent、processGenericMotionEvent方法中都有一句很关键的一句代码;
mView.dispatchGenericMotionEvent(event)
可以看到mView并没有实例化,其实他的实例化在ViewRootImpl的setView方法中,
可以看出,mView的实例化是在setView方法中完成,而我们知道ViewRootImpl的setView方法中传入的view参数是DecorView,因为ViewRootImpl通过setView方法将DecorView添加到PhoneWindow的。
所以这里的mView其实就是DecorView。
这样一来,可以知道ViewPostImeInputStage将事件分发到了View,而这里的mView又是DecorView,也就是多态的原理,如果DecorView没有上述的mView.的几个方法,就会调用View的方法,如果DecorView实现了就会调用DecorView的方法,继续跟踪源码,其中在View的dispatchPointerEvent方法如下:
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); }}
会调用dispatchTouchEvent方法,根据多态的特性,最终点击事件会传给给DecorView的dispatchTouchEvent方法。下面再看DecorView的dispatchTouchEvent方法。
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) { final Callback cb = getCallback(); return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);}可以看出DecorView最终会调用cb.dispatchTouchEvent方法,那么问题问题又来了,这个Callback是什么,其实这个Callback就是当前的Activity。
首先Activity继承了Window.Callback
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback {然后Window.Callback的代码如下:
public interface Callback { /** * Called when action mode is first created. The menu supplied will be used to * generate action buttons for the action mode. * * @param mode ActionMode being created * @param menu Menu used to populate action buttons * @return true if the action mode should be created, false if entering this * mode should be aborted. */ public boolean onCreateActionMode(ActionMode mode, Menu menu); /** * Called to refresh an action mode's action menu whenever it is invalidated. * * @param mode ActionMode being prepared * @param menu Menu used to populate action buttons * @return true if the menu or action mode was updated, false otherwise. */ public boolean onPrepareActionMode(ActionMode mode, Menu menu); /** * Called to report a user click on an action button. * * @param mode The current ActionMode * @param item The item that was clicked * @return true if this callback handled the event, false if the standard MenuItem * invocation should continue. */ public boolean onActionItemClicked(ActionMode mode, MenuItem item); /** * Called when an action mode is about to be exited and destroyed. * * @param mode The current ActionMode being destroyed */ public void onDestroyActionMode(ActionMode mode);}所以Activity必须要实现Window.Callback当中的方法,基本都是事件传递相关的,其中就是dispatchTouchEvent方法,在Activity的 attach方法中有如下一段代码:
PhoneWindow通过设置setCallback将Callback设置为this也就是Activity。
至此点击事件已经到了Activity,那么后面就和上面博客事件分发机制一样了。事件分发机制
原链接
- Android中的ViewRootImpl类源码解析
- Android中的ViewRootImpl类源码解析
- Android中的ViewRootImpl类源码解析
- Android中的ViewRootImpl类源码解析
- Android中的ViewRootImpl类源码解析
- ADT eclipse下如何查找Android源码ViewRootImpl类
- ViewRootImpl解析
- android中如何使用ViewRootImpl这个类
- android中如何使用ViewRootImpl这个类
- android中如何使用ViewRootImpl这个类
- Android View源码解读:浅谈DecorView与ViewRootImpl
- Android View源码解读:浅谈DecorView与ViewRootImpl
- Android View源码解读:浅谈DecorView与ViewRootImpl
- Android View源码解读:浅谈DecorView与ViewRootImpl
- Android View源码解读:浅谈DecorView与ViewRootImpl
- android.view.ViewRootImpl$CalledFromWrongThreadException:
- android.view.ViewRootImpl$CalledFromWrongThreadException
- android.view.ViewRootImpl$CalledFromWrongThreadException
- 伪装在系统PAM配置文件中的同形异义字后门
- spring quartz在项目中的应用实例
- java多线程之延迟初始化
- bootstrap table 导出
- 黄油刀butterknife Android studio
- Android中的ViewRootImpl类源码解析
- Entity Framework 学习初级篇1--EF基本概况
- javaScript解决浏览器兼容问题,判断浏览器是ie或者Chrome
- hibernate @OneToMany 级联删除
- spark streaming程序因集群kafka版本不一致造成ZkUtils类无法更新offset解决方案
- 【Linux基础系列之】内存管理(1)-buddy和slub算法
- HTML常用 注意
- 【TensorFlow-windows】(六) CNN之Alex-net的测试
- FFMPEG结构体分析:AVFormatContext