文章目錄
前言
系列總結回顧:
ViewGroup事件分發總結-TouchTarget
ViewGroup事件分發總結-多點觸摸事件拆分
關於Android觸摸事件分發機制,在日常應用層開發工作中最常接觸的是ViewGroup中的事件派發。當應用窗口接收到系統傳來的Event後,到將Event傳入ViewGroup前還有一段路程,本文就來分析下這個過程。
源碼探究
文中源碼基於 Android 10.0
事件監聽註冊
APP側想要從系統接收觸摸事件,首先需要進行“接收器”的註冊。我們知道Acitivty啓動在onResume之後,會創建ViewRootImpl,然後調用它的setView方法向WindowManagerService添加窗口,而“接收器”的註冊就是在這個階段。
[ViewRootImpl#setView]
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
// ···
// 創建InputChannel用於輸入事件的傳輸
mInputChannel = new InputChannel();
// ···
// 添加給WindowManagerService
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
// ···
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
// 存儲事件的隊列
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// 初始化並註冊輸入事件接收器
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
// ···
}
}
}
該方法中幾個比較關鍵的步驟:
- 首先創建InputChannel,它用於系統進程傳輸輸入事件給APP進程。
- 接着將InputChannel傳給WindowManagerService,WMS會對InputChannel進行初始化。
- 創建用於存儲系統傳來的事件的隊列。
- 創建InputEventReceiver,並傳入InputChannel和主線程Looper,在構造函數中進行註冊監聽。
InputChannel的初始化
接下來看其中第二步驟,WindowSession#addToDisplay會調用WindowManagerService的addWindow方法:
[WindowManagerService#addWindow]
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
// ···
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
// 打開InputChannel(win是WindowState)
win.openInputChannel(outInputChannel);
}
// ···
}
win的實例是WindowState,一個WindowState表示一個應用端窗口。
[WindowState#openInputChannel]
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
// 創建native層InputChannel數組(包含兩個InputChannel)
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// 第一個用於輸出事件
mInputChannel = inputChannels[0];
// 第二個用於接收事件
mClientChannel = inputChannels[1];
// InputWindowHandle作爲具柄提供給InputManagerService,用來尋找目標窗口。
// IWindow對應一個應用側窗口,將其作爲token。
mInputWindowHandle.token = mClient.asBinder();
// outInputChannel即在應用進程創建傳過來的InputChannel
if (outInputChannel != null) {
// 將mClientChannel中的實例狀態轉移到outInputChannel中
mClientChannel.transferTo(outInputChannel);
// 釋放mClientChannel中的引用
mClientChannel.dispose();
mClientChannel = null;
} else {
// If the window died visible, we setup a dummy input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
// 將mInputChannel和IWindow註冊給InputManagerService保存
mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}
該方法中首先調用native方法打開兩個InputChannel,一個用於系統端寫入事件,一個用於應用端讀取事件(當事件處理完畢後,應用端InputChannel會用來寫入反饋,系統端InputChannel用來讀取反饋)。之後將用於發送事件的InputChannel註冊給InputManagerService,將用於接收事件的InputChannel傳回給應用窗口。
打開InputChannel
這裏的打開InputChannel指的是什麼呢?進入native層方法看看:
[android_view_InputChannel#android_view_InputChannel_nativeOpenInputChannelPair]
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
std::string name = nameChars;
env->ReleaseStringUTFChars(nameObj, nameChars);
// serverChannel對應前文中的mInputChannel,clientChannel對應前文中的mClientChannel
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
// 分配serverChannel和clientChannel
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
// 創建Java層InputChannel數組
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
// 創建Java層InputChannel,將serverChannel的指針保存在InputChannel.mPtr成員中
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
// 同上
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
// 返回InputChannel數組
return channelPair;
}
可以看到這裏創建了native層的InputChannel,再將指針保存在Java層InputChannel.mPtr成員中。
看其中的InputChannel::openInputChannelPair方法:
[InputTransport#InputChannel::openInputChannelPair]
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
// 創建一對socketpair,保存在sockets中
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
// 錯誤處理 ···
return result;
}
// 設置sockopt讀寫緩存區
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
std::string serverChannelName = name;
serverChannelName += " (server)";
// 創建Server端InputChannel,並持有其中一個socketpair
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
std::string clientChannelName = name;
clientChannelName += " (client)";
// 創建Client端InputChannel,並持有其中一個socketpair
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
該方法中創建一對socketpair,這對socketpair中含有兩個相通的文件描述符fd,通過向fd寫數據和讀數據實現數據通信。Server端InputChannel和Client端InputChannel各持有一個socketpair,達到互相之間的雙向通信。
關於socketpair的詳細解釋可以參考《Android中socketpair雙向通信詳解》
到這裏我們知道InputChannel其實是對socketpair的一層封裝。
APP側InputChannel
回到WindowState#openInputChannel方法中,在完成包含socketpair的InputChannel對的創建後,需要將其中一個交給APP側,另一個交給InputManagerService。
通過InputChannel.transferTo方法將內部引用狀態轉移給之前在APP側創建和傳過來的InputChannel,該方法中調用了nativeTransferTo方法:
[android_view_InputChannel.cpp]
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
// 通過Java層InputChannel.mPtr保存的地址獲取native層InputChannel對象
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
// 將nativeInputChannel的地址設置給APP側Java層InputChannel.mPtr
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
// 清空自身的InputChannel.mPtr的值
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
轉移對象就是通過更改InputChannel.mPtr的值來實現。
IMS側InputChannel
當轉移完對象後,就會進行InputManagerService側的註冊,在registerInputChannel方法中又會調用nativeRegisterInputChannel方法,傳入InputChannel。
[com_android_server_input_InputManagerService.cpp]
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jint displayId) {
// 獲取native層InputManagerService
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
// 獲取native層InputChannel
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
// ···
// 註冊進native層InputManagerService
status_t status = im->registerInputChannel(env, inputChannel, displayId);
// ···
}
該方法中取出native層對應的對象,native層InputManagerService的registerInputChannel方法中會把InputChannel註冊給它的InputDispatcher。
接着看InputDispatcher#registerInputChannel方法:
[InputDispatcher.cpp]
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
int32_t displayId) {
// ···
{ // acquire lock
std::scoped_lock _l(mLock);
// ···
// 創建Connection封裝InputChannel
sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
// 獲取InputChannel中socketpair的文件描述符
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
// Looper監聽fd可讀事件,當InputChannel對端寫入數據時會觸發handleReceiveCallback回調
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
該方法中創建Connection用來封裝InputChannel,並保存在mConnectionsByFd和mInputChannelsByToken集合中。並讓InputDispatcherThread中創建的Looper監聽InputChannel中fd的可讀事件。
Connection中有幾個重要成員:
- InputPublisher inputPublisher:持有InputChannel,用於操作InputChannel進行寫入事件和讀取反饋。
- Queue outboundQueue:緩存等待寫入InputChannel進行發送的事件。
- Queue waitQueue:緩存已發送給應用窗口的事件,用於等待事件完成反饋。
到這裏便完成了InputChannel的初始化,創建了一對InputChannel分別保存在InputManagerService側和應用窗口側,從而實現傳輸鏈路的連接,之後雙方可以通過InputChannel中的socketpair進行雙向通信。
InputEventReceiver的初始化
InputEventReceiver用於APP側接收到數據後響應事件回調。回到ViewRootImpl#setView中,這裏創建其子類WindowInputEventReceiver,並傳入初始化完成的InputChannel和主線程Looper。
在InputEventReceiver的構造函數中,會利用InputChannel和主線程Looper中的MessageQueue進行初始化操作,其中會調用nativeInit方法:
[android_view_InputEventReceiver.cpp]
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
// 通過InputChannel.mPtr成員獲得native層InputChannel對象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
// ···
// 通過MessageQueue.mPtr成員獲得native層MessageQueue對象
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
// ···
// 創建native層InputEventReceiver,封裝Java層InputEventReceiver、InputChannel、MessageQueue
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
// 初始化,使Looper監聽fd中可讀事件
status_t status = receiver->initialize();
// ···
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
// 返回native層InputEventReceiver指針
return reinterpret_cast<jlong>(receiver.get());
}
該方法中主要是創建了native層的InputEventReceiver,並返回其地址給Java層保存。在它的初始化操作中,使用MessageQueue對應的Looper監聽InputChannel中fd的ALOOPER_EVENT_INPUT可讀事件。
小結
輸入事件“接收器”的註冊過程,其實就是創建了一對InputChannel,分別保存在InputManagerService側和應用窗口側,利用其進行雙端數據傳輸。然後又創建WindowInputEventReceiver,用於應用窗口側在接收到事件後,進一步分發處理。
輸入事件接收
這裏不討論InputManagerService中的事件發送流程,僅從觸發事件接收開始分析。
當InputManagerService通過InputChannel的fd寫入事件數據後,應用窗口側的InputChannel就會產生可讀事件,將喚醒應用側Looper(NativeInputEventReceiver初始化時註冊了fd監聽和回調),然後觸發LooperCallback的handleEvent回調(NativeInputEventReceiver繼承LooperCallback)。
[android_view_InputEventReceiver.cpp]
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
// ···
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
// ···
}
判斷類型如果是ALOOPER_EVENT_INPUT,則調用consumeEvents方法進一步處理。
[android_view_InputEventReceiver.cpp]
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
// ···
// 循環不斷的處理(如果有事件)
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
// 從InputChannel中讀取一條數據,封裝在InputEvent中
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
// ···
if (!skipCallbacks) {
// ···
jobject inputEventObj;
// 判斷事件類型,創建對應事件對象
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
// 按鍵類型事件
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
case AINPUT_EVENT_TYPE_MOTION: {
// 觸摸類型事件
MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
default:
assert(false); // InputConsumer should prevent this from ever happening
inputEventObj = NULL;
}
if (inputEventObj) {
// 調用Java層方法分發事件對象
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
// ···
}
}
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
可以看到,這裏面循環不斷的從InputChannel中讀取數據封裝成InputEvent對象,並根據事件類型(按鍵、觸摸)創建對應的事件對象,之後調用Java層WindowInputEventReceiver的dispatchInputEvent方法進行事件派發。
小結
事件接收是通過先前註冊的主線程Looper監聽fd來實現,收到事件後封裝成對應類型的觸摸事件,調用Java層InputEventReceiver#dispatchInputEvent方法傳遞事件對象進行派發。
應用窗口事件派發
隊列分發
[InputEventReceiver#dispatchInputEvent]
private void dispatchInputEvent(int seq, InputEvent event) {
// 保存event序列號,用於事件處理完畢後再通知InputManagerService
mSeqMap.put(event.getSequenceNumber(), seq);
// 處理事件
onInputEvent(event);
}
WindowInputEventReceiver重寫了onInputEvent方法:
[WindowInputEventReceiver#onInputEvent]
public void onInputEvent(InputEvent event) {
// ···
// 事件入隊
enqueueInputEvent(event, this, 0, true);
// ···
}
這裏調用ViewRootImpl的enqueueInputEvent方法先將事件入隊。
[ViewRootImpl#enqueueInputEvent]
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
// 從對象緩存池獲取一個QueuedInputEvent,封裝InputEvent
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
// 加入隊列尾
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
// 此時processImmediately爲true,表示立即處理
if (processImmediately) {
// 直接開始處理
doProcessInputEvents();
} else {
// 通過handler發送一個MSG_PROCESS_INPUT_EVENTS消息處理
scheduleProcessInputEvents();
}
}
加入隊列後就立即開始處理。
接着看doProcessInputEvents方法:
[ViewRootImpl#doProcessInputEvents]
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
// 遍歷事件隊列
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
// ···
// 依次分發隊列頭事件
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
// 事件全部分發完畢,取消MSG_PROCESS_INPUT_EVENTS消息
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
該方法中會依次將隊列中的事件全部進行分發。
[ViewRootImpl#deliverInputEvent]
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
// ···
InputStage stage;
// ···
// 獲取InputStage
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
// ···
if (stage != null) {
handleWindowFocusChanged();
// 由InputStage進行具體的事件分發處理
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
該方法中獲取InputStage後,由InputStage處理事件分發。
InputStage
InputStage是事件分發責任鏈中的一個基本單元。輸入事件可能會經過多個InputStage,直到有InputStage攔截處理,或者都沒有處理。
InputStage責任鏈的設置也是在ViewRootImpl的setView分發中:
[ViewRootImpl#setView]
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// ···
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
// 綜合處理未分發的事件
mSyntheticInputStage = new SyntheticInputStage();
// 分發給view hierarchy,優先級在輸入法之後
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
// 分發給native activity,優先級在輸入法之後
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
// 優先級在輸入法之後
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
// 分發給輸入法(不支持觸摸事件)
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
// 分發給view hierarchy,優先級在輸入法之前(不支持觸摸事件)
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
// 分發給native activity,優先級在輸入法之前(不支持觸摸事件)
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
// mFirstInputStage是優先級最高的InputStage
mFirstInputStage = nativePreImeStage;
// mFirstPostImeInputStage是輸入法之後優先級最高的InputStage
mFirstPostImeInputStage = earlyPostImeStage;
}
可以看到通過組合包裝的方式創建了InputStage責任鏈,越早創建的InputStage越後執行。
這裏關心ViewPostImeInputStage,它負責分發事件到視圖樹,看它的deliver分發:
[ViewPostImeInputStage#deliver]
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
// 如果標記FLAG_FINISHED則轉到下一個InputStage
forward(q);
} else if (shouldDropInputEvent(q)) {
// 如果需要丟棄則標記FLAG_FINISHED,然後轉給下一個InputStage
finish(q, false);
} else {
// onProcess分發處理事件並返回是否處理的結果,apply會根據結果繼續向下轉發
apply(q, onProcess(q));
}
}
直接看onProcess方法:
[ViewPostImeInputStage#onProcess]
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) {
// 觸摸事件
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
// 軌跡球事件
return processTrackballEvent(q);
} else {
// 其他通用事件(例如外設)
return processGenericMotionEvent(q);
}
}
}
該方法中根據不同事件類型,調用不同方法處理事件。
這裏看觸摸事件的處理,進入processPointerEvent方法:
[ViewPostImeInputStage#processPointerEvent]
private int processPointerEvent(QueuedInputEvent q) {
// 事件對象轉換成MotionEvent
final MotionEvent event = (MotionEvent)q.mEvent;
// ···
// 調用了View的dispatchPointerEvent方法
boolean handled = mView.dispatchPointerEvent(event);
// ···
return handled ? FINISH_HANDLED : FORWARD;
}
mView就是在ViewRootImpl#setView時傳入的DecorView,這裏調用了它的dispatchPointerEvent方法進行MotionEvent的分發。
DecorView派發過程
當MotionEvent傳給DecorView後,還沒有直接派發給我們設置的視圖樹。
dispatchPointerEvent方法中又調用了dispatchTouchEvent方法:
[DecorView#dispatchTouchEvent]
public boolean dispatchTouchEvent(MotionEvent ev) {
// 獲取PhoneWindow中的Callback
final Window.Callback cb = mWindow.getCallback();
// 正常情況下會執行cb.dispatchTouchEvent(ev)
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
可以看到DecorView並不會直接把MotionEvent派發給Content View,而是轉發給Window.Callback,這個Callback就是Activity實例(Activity實現了Window.Callback接口,Activity在attach方法中創建PhoneWindow時設置Callback)。
進入Activity:
[Activity#dispatchTouchEvent]
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 又轉發給PhoneWindow處理
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
該方法中又優先將MotionEvent交由PhoneWindow處理,若沒有處理則再通過onTouchEvent由自身處理。
進入PhoneWindow:
[PhoneWindow#superDispatchTouchEvent]
public boolean superDispatchTouchEvent(MotionEvent event) {
// 由調用了DecorView
return mDecor.superDispatchTouchEvent(event);
}
這裏又調用了DecorView,不同的是調用的是superDispatchTouchEvent方法,該方法中會調用super.dispatchTouchEvent,即ViewGroup的dispatchTouchEvent方法。從這裏開始就正式將MotionEvent傳入ViewGroup,開始ViewGroup的事件派發流程。
小結
尾聲
一個觸摸事件在派發給ViewGroup前,還經過了InputManagerService-APP窗口-窗口根視圖的漫長流程,其中事件傳輸的關鍵就是InputChannel和InputEventReceiver。