Android事件分发始末

        要谈Android的事件分发还需从Activity的启动流程谈起,不然事件分发的回溯会让人看的一头雾水。我们都知道,Android的Activity启动时,启动函数为ActivityThread类下的handleLaunchActivity()方法。启动时,先调用了一个静态方法

WindowManagerGlobal.initialize();   

WindowManagerGlobal可以看做是WindowManager的代理类。该初始化方法中主要是通过Binder机制获取了WindowManagerService对象。

在WindowManagerGlobal初始化完毕后才通过反射创建Activity实体。

创建Activity

然后调用Activity的attach方法,此方法中为该Activity创建了Window的实现类PhoneWindow,为Window对象设置了callback对象为Activity自己。注意此处很重要。因为Activity实现了Window的Callback接口


Acticity类的attach方法

在attach()的末尾,为mWindow对象创建了WindowManager对象,同时Activity对象自己也保留该对象的引用。

创建WindowManager

绑定方法完毕后,开始执行Activity的onCreate()方法。注意,此时需要进入onCreate()方法中我们耳熟能详的setContentView()方法。此处也很重要,不进入该方法,我们就会“迷失”在源码中。

调用onCreate()方法

那么接下来,我们的任务就是跟踪setContentView()方法,看看这里又做了些什么。


Activity的onCreate()方法

我们看到实际上Activity调用的Window的setContentView方法,而从前面的代码我们知道,Window这个抽象类的实现类为PhoneWindow.所以此时,我们只需要看下PhoneWindow对于setContentView的实现。实现代码如下:


PhoneWindow的setContentView()

这里有一个非常重要的方法,就是installDecor()方法,这个方法里,新建了Activity的实际根View,DecorView,我们设置的layoutResID实际上是被添加到DecorView里去了。到这里,我们的performLaunch方法就结束了。接下来,我们要回到ActivityThread中,进入接下来的handleResumeActivity()方法。


handleResumeAcitivity()重要代码片段

注意在这里,我们真正执行了添加view的操作。也就是截图所示的最后一行,wm.addView(decor,l)。这里的WindowManager就是前文提到的WindowManager对象,他的实现类为WindowManagerImpl,而实际上,在WindowManagerImpl中又持有一个WindowManagerGlobal代理类。在实际的addView操作中,该方法调用了其代理类的方法。

WindowManagerImpl的addView

当我们追进到WindowMangerGlobal的addView方法中之后,我们才知道,原来真正的实现还不是他!其实真正实现添加View操作的是ViewRootImpl对象的setView方法。

WindowManagerGlobal的addView

最后,我们就需要跟进ViewRootImpl,看看他到底做了些什么。实际上,我们要关注的是他做了两件事,第一件事,把DecorView对象赋值给力mView,第二是更新布局:

申请布局

checkThread中会抛出我们常见的不在UIThread的异常。所以通过源码我们可以知道,在resume执行之前,我们其实是可以在非ui线程操作view的!而scheduleTraversals方法会异步执行测量和布局工作。布局完成后,还会通过mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();来通知我们测量完成,我们可以获得各个子View的宽高了。

异步执行测量和布局和绘制工作

所以最后,我们知道,所有的view其实都是通过ViewRootImpl来控制的,正如该类的注释所说:ViewRootImpl是View树的顶点,他是View和WindowManager的桥梁。

ViewRootImpl的注释

梳理完了创建流程之后,我们才能更有的放矢地观察事件分发流程。在ViewRootImpl中,有一个WindowInputEventReceiver,这个接受者主要用来从InputChannel中接收输入事件。

WindowInputEventReceiver

他继承自InputEventReceiver。该Receiver中有一个重要的方法,dispatchInputEvent,注意看注释,该方法会由C++代码调用。当调用后,该方法会调用onInputEvent()回调,我们所有的处理在onInputEvent里处理就好了

分发事件

那为什么Native方法会可以调用到这个方法呢?我们可以通过InputEventReceiver的构造函数找到原因:


InputEventReceiver构造方法

从构造方法中,我们看到了nativeInit方法,它通过InputChannel创建了一个沟通Java层和Native层的通道。

当ViewRootImpl接收到屏幕上的输入事件后,执行了enqueueInputEvent()方法,而在方法中,又通过判断消息的及时性,执行了doProcessInputEvents()方法,方法中关键为InputStage的deliverInputEvent()方法。在若干个InputStage中,我们需要关注的ViewPostImeInputStage,这个才是分发屏幕事件的类。

InputStage的deliver方法,注意apply()

在ViewPostImeInputStage中,我们要重点关注对onProcess的实现:


onProcess()

如果是屏幕的点击事件,会走到processPointerEvent()中。方法的具体实现如下:


具体处理点击事件的方法

这里调用了mView.dispatchPointerEvent()方法,那这个mView是什么呢?通过前文的梳理,我们可以知道这个mView就是在创建时我们传入的DecorView对象!那么这个时候我们就要进入DecorView的分发事件方法中去一看究竟了。这里需要注意的是:dispatchPointerEvent()方法是View的方法,我们需要先看看View里对这个方法的实现:

View中的事件分发方法

我们需要关注的是屏幕点击事件,所以我们要跟进的dispatchTouchEvent方法,现在看到这个方法是不是很眼熟了?我们接着查看DecorView对dispatchTouchEvent的实现:

DecorView的分发方法

此时,我们发现他调用的Window.Callback的dispatchTouchEvent()。那么这个Callback又是什么呢?回到前文我们可以知道,DecorView的Window成员变量的Callback,就是在Avtivity实例化后进行attach()操作时设置的,就是Activity本身。所以这里的Window.Callback就是当前的Activity了。所以在做事件分发时,我们的源头一般都是在Activity中重写dispatchTouchEvent()方法。

至此,Activity是如何接受到Touch事件的,我们已经搞清楚了。下一篇,我们接着看接收到事件后,Touch事件又是如何流转的呢?

你可能感兴趣的:(Android事件分发始末)