網(wǎng)上關(guān)于Android事件分發(fā)機(jī)制的資料有許多,看過(guò)很多次,但是每次過(guò)一段時(shí)間就會(huì)忘記,感覺還是自己研究的不夠深入,這一次,決定自己根據(jù)源碼,來(lái)好好梳理一遍Android事件分發(fā)機(jī)制的知識(shí),本篇文章講的主要是一個(gè)觸摸事件,如何傳入到Activity中。
事件的入口
首先是有一個(gè)疑問(wèn),我們討論事件分發(fā),那么究竟事件的來(lái)源是哪里呢?我們知道Activity的dispatchTouchEvent(MotionEvent ev)是會(huì)接受到事件的,所以我們?cè)谠摲椒ㄖ姓{(diào)用Thread.dumpStack()來(lái)查看調(diào)用棧。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Thread.dumpStack();
return super.dispatchTouchEvent(ev);
}
運(yùn)行程序,輸出結(jié)果為:
at java.lang.Thread.dumpStack(Thread.java:490)
at com.dpal.javademo.MainActivity.dispatchTouchEvent(MainActivity.java:30)
at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68)
at com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2364)
at android.view.View.dispatchPointerEvent(View.java:9539)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4281)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4144)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3683)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3736)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3702)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3828)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3710)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3885)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3683)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3736)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3702)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3710)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3683)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5973)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5947)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5908)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6079)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:195)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
at android.os.Looper.loop(Looper.java:141)
at android.app.ActivityThread.main(ActivityThread.java:5653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:746)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)
具體分析
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6079)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:195)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:323)
我們知道Android的消息機(jī)制是Handler機(jī)制,通過(guò)將消息封裝到Message中,再發(fā)送到消息所在Handler所在MessageQueue中,并且Looper不斷調(diào)用MessageQueue的next()方法進(jìn)行消息的處理。所以根據(jù)上面的調(diào)用信息,當(dāng)我們觸摸屏幕時(shí),nativePollOnce()將會(huì)收到消息,并且將事件發(fā)送給InputEventReceiver的dispatchInputEvent()方法
直接從名字我們應(yīng)該能看出它的作用,輸入事件的接受者。我們?cè)賮?lái)看看官方對(duì)InputEventReceiver的描述
/**
* Provides a low-level mechanism for an application to receive input events.
* 提供應(yīng)用程序接收輸入事件的低級(jí)機(jī)制。
* @hide
*/
public abstract class InputEventReceiver {
/**
* Creates an input event receiver bound to the specified input channel.
* 創(chuàng)建綁定到指定輸入通道的輸入事件接收器。
* @param inputChannel The input channel.
* @param looper The looper to use when invoking callbacks.
*/
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
/**
* Called when an input event is received.
* 當(dāng)接收到輸入事件時(shí)調(diào)用。
*
* @param event The input event that was received.
*/
public void onInputEvent(InputEvent event) {
finishInputEvent(event, false);
}
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
private void dispose(boolean finalized) {
if (mCloseGuard != null) {
if (finalized) {
mCloseGuard.warnIfOpen();
}
mCloseGuard.close();
}
if (mReceiverPtr != 0) {
nativeDispose(mReceiverPtr);
mReceiverPtr = 0;
}
mInputChannel = null;
mMessageQueue = null;
}
}
我省略了一些不關(guān)鍵的代碼,果然,InputEventReceover提供了應(yīng)用程序接受輸入事件的低級(jí)機(jī)制,在它的構(gòu)造器中,我們看到了一個(gè)nativeInit()方法的調(diào)用,這里系統(tǒng) native 層就會(huì)將這個(gè)InputEventReceiver實(shí)例記錄下來(lái),每當(dāng)有事件到達(dá)時(shí)就會(huì)通過(guò)inputChannel管道派發(fā)到這個(gè)實(shí)例上,當(dāng)然還有注銷的方法:dipose()。在InputEventReceiver中,我們看到dispatchInputEvent()方法注釋著從native層代碼調(diào)用,也就是nativePollOnce()內(nèi)部會(huì)調(diào)用這個(gè)方法。
大家注意這里的InputEventReceiver是一個(gè)抽象類,再根據(jù)棧中的信息,事件將會(huì)傳到ViewRootImpl$WindowInputEventReceiver.onInputEvent()中,說(shuō)明nativePollOnce()其實(shí)調(diào)用的是InputEventReceiver的子類WindoInputEventReceiver的dispatchInputEvent()方法,然后再調(diào)用onInputEvent()方法。
而WindowInputEventReceiver是ViewRootimpl的一個(gè)內(nèi)部類,我們來(lái)看一下他的源碼:
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
···
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
//將event事件,receiver,flags包裝成一個(gè)QueuedInputEvent
//QueuedInputEvent表示一個(gè)在隊(duì)列中等待處理的輸入事件,這個(gè)類有個(gè)next屬性可以指向下一個(gè)事件
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//獲得等待隊(duì)列的最后一個(gè)輸入事件(Pending的意思是等待的,Tail的意思是尾部)
QueuedInputEvent last = mPendingInputEventTail;
//下面的意思就是將事件加入到隊(duì)列中
if (last == null) {
//如果沒有最后一個(gè),就說(shuō)明隊(duì)列是空的,那么第一個(gè)是該事件,最后一個(gè)也是該事件
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
//如果有最后一個(gè),那么就將該事件設(shè)置成最后一個(gè)
last.mNext = q;
mPendingInputEventTail = q;
}
//隊(duì)列數(shù)量加1
mPendingInputEventCount += 1;
//事件跟蹤機(jī)制。。。不需要管
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
//如果事件需要立即處理,則執(zhí)行doProcessInputEvents(),
//WindowInputEventReceiver中enqueueInputEvent(event, this, 0, true);傳入的是true
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
···
}
省略了一些不關(guān)鍵的代碼,我們看到事件通過(guò)WindowInputEventReceiver的onInputEvent()方法傳遞到了ViewRootImpl的enqueueInputEvent()中。在enqueueInputEvent()中,我已經(jīng)注釋了每一步的作用,事件將會(huì)傳遞到doProcessInputEvents()方法中,我們?cè)賮?lái)看這個(gè)方法的源碼:
void doProcessInputEvents() {
// 處理隊(duì)列中所有的輸入事件
while (mPendingInputEventHead != null) {
//下面這段代碼是取出事件隊(duì)列中的第一個(gè),若有第二個(gè),將其置為第一個(gè)
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
//隊(duì)列數(shù)量減1
mPendingInputEventCount -= 1;
//跟蹤事件,不需要管
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
//下面的代碼是獲得當(dāng)前事件的發(fā)生時(shí)間,以及此事件與上一個(gè)事件間隔間隔時(shí)間
//通過(guò)Choreographer,協(xié)調(diào)動(dòng)畫、輸入和繪圖的時(shí)間
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
deliverInputEvent(q);
}
// 處理完了所有的輸入事件,將處理事件等待標(biāo)記設(shè)為false
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
Ok,我們看到在doProcessInputEvents()方法中,輸入事件從一個(gè)一個(gè)從隊(duì)列中被取出,并傳入deliverInputEvent()方法中,這一點(diǎn)完全和文章開頭的調(diào)用棧信息相同:
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5973)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5947)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5908)
我們繼續(xù)說(shuō)deliverInputEvent()方法,通過(guò)方法名,這個(gè)方法的作用應(yīng)該是傳遞輸入事件的吧?究竟是不是呢?我們繼續(xù)探究它的代碼:
private void deliverInputEvent(QueuedInputEvent q) {
//跟蹤機(jī)制,不用管
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
//一致性驗(yàn)證,不用管,一致性驗(yàn)證就是比如說(shuō)判斷ACTION_DOWN和ACTION_UP是否成對(duì)出現(xiàn)
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
在這個(gè)方法中,得到了一個(gè)InputStage對(duì)象。它是處理輸入事件的一個(gè)階段,可以將事件完成或者轉(zhuǎn)送到下一個(gè)階段。InputStage分為多種,例如SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage等等,Android在這里使用了設(shè)計(jì)模式中的責(zé)任鏈模式,多個(gè)InputStage連成一條鏈,并沿著這條鏈傳遞輸入事件,直到有一個(gè)InputStage處理了該輸入事件。觸摸事件就是由ViewPostImeInputStage處理,這一點(diǎn)也可以通過(guò)文章一開始的調(diào)用棧輸出信息來(lái)確認(rèn),事件最終傳遞給了ViewPostImeInputStage中的onProcess()然后傳遞給processPointerEvent()方法:

我們先來(lái)看看ViewPostImeInputStage中的onProcess()方法:
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
//按鍵事件,比如回退鍵
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
//普通的觸摸點(diǎn)事件
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
//軌跡球事件,我并不清楚軌跡球是啥玩意,應(yīng)該是下面那張軌跡球的圖片把?
return processTrackballEvent(q);
} else {
//滾輪事件,比如外接藍(lán)牙鼠標(biāo)時(shí),可以觸發(fā)滾輪事件
return processGenericMotionEvent(q);
}
}
}
在onProcess()方法中,根據(jù)判斷,分了四種情況處理輸入事件,具體的分類我在注釋中已經(jīng)給出,本文我們只分析普通的觸摸事件,也就是分析processPointerEvent()方法。

我們?cè)賮?lái)看processPointerEvent()方法的代碼:
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
//最關(guān)鍵的代碼?。。。? boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
來(lái)吧,讓我們看到最關(guān)鍵的代碼,總算出現(xiàn)了View的蹤跡,其中的mView就是我們熟悉的DecorView了。為什么mView就是DecorView呢?大家可以參考這篇文章Android View源碼解讀:淺談DecorView與ViewRootImpl,我們進(jìn)入DecorView中的dispatchPointerEvent()方法中。。。咦?你是否真的在DecorView中找dispatchPointerEvent()方法了?哈哈哈,是不是找不到?當(dāng)然了!這個(gè)方法其實(shí)是在View類里面:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
好吧,很簡(jiǎn)單的幾行代碼,如果事件屬于觸摸事件,就調(diào)用dispatchTouchEvent(event)方法,如果不是,則調(diào)用dispatchGenericMotionEvent(event)方法。讓我們繼續(xù)跟隨事件,進(jìn)入DecorView中的dispatchTouchEvent(),如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
//mFeatureId :面板的特征ID,如果這是應(yīng)用程序的DecorView就為-1,在初始化時(shí)設(shè)置
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
在這個(gè)方法中,mWindow就是與Activity關(guān)聯(lián)的PhoneWindow對(duì)象,由于DecorView是PhoneWindow創(chuàng)建的,并且通過(guò)setWindow()方法,DecorView對(duì)象持有了PhoneWindow對(duì)象的引用。通過(guò)getCallback()方法,就獲得了Window.Callback對(duì)象,Window.Callback包含了窗口的各種回調(diào)接口,Activity就實(shí)現(xiàn)了該接口。根據(jù)return后的判斷,當(dāng)調(diào)用cb.dispatchTouchEvent(ev)時(shí),其實(shí)調(diào)用的就是Activity中的dispatchTouchEvent()方法。接下來(lái)就是從Activity出發(fā),進(jìn)一步分析事件分發(fā)機(jī)制了。

終于好了,讓我們來(lái)總結(jié)一下吧
- 首先,當(dāng)我們觸摸屏幕時(shí),通過(guò)Android消息機(jī)制,從Looper從MessageQueue中取出該事件,發(fā)送給WindowInputEventReceiver。
- WindowInputEventReceiver是ViewRootImpl的內(nèi)部類,通過(guò)enqueueInputEvent方法,將輸入事件加入輸入事件隊(duì)列中,并進(jìn)行處理和轉(zhuǎn)發(fā)。
- ViewPostImeInputStage收到輸入事件,將事件傳遞給DecorView的dispatchPointerEvent()方法(是View的方法)
- dispatchPointerEvent()方法通過(guò)DecorView中的dispatchTouchEvent()方法,調(diào)用了Activity的dispatchTouchEvent()方法。
到此事件進(jìn)入Activity中?。。。∵M(jìn)入Activity后,事件又是如何被處理的呢?可以查看Android事件分發(fā)機(jī)制及源碼分析