本篇只會講到觸發(fā)ViewGroup的dispatchTouchEvent為止,因為接下去的一搜一大把,或者說有點基礎(chǔ)的應(yīng)該都了解。
-
從觸摸開始
首先腐晾,請你打開命令行工具叉弦。輸入
adb shell
進(jìn)入到shell命令,然后輸入getevent
藻糖,會監(jiān)聽打印觸摸屏幕的event信息淹冰。add device 1: /dev/input/event5 name: "msm8974-taiko-mtp-snd-card Headset Jack" add device 2: /dev/input/event4 name: "msm8974-taiko-mtp-snd-card Button Jack" add device 3: /dev/input/event3 name: "hs_detect" add device 4: /dev/input/event1 name: "touch_dev" add device 5: /dev/input/event0 name: "qpnp_pon" add device 6: /dev/input/event2 name: "gpio-keys"
當(dāng)你使出你的一陽指點擊屏幕的時候,變回不斷的去獲取到你的點擊事件巨柒,就像這樣:
/dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0039 000005cf /dev/input/event1: 0003 0035 0000020b /dev/input/event1: 0003 0036 0000068d /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0036 0000068c /dev/input/event1: 0003 0030 00000005 /dev/input/event1: 0000 0000 00000000 /dev/input/event1: 0003 0039 ffffffff /dev/input/event1: 0000 0000 00000000
這些操作全都是Linux Kernel去做的樱拴,只要你點擊了屏幕了,硬件設(shè)備變回產(chǎn)生硬件終端洋满,Kernel收到硬件終端之后晶乔,會對其進(jìn)行加工,包裝成event事件之后添加到/dev/input/目錄下牺勾,就像如上所示的event1瘪弓。
-
Android系統(tǒng)的監(jiān)聽
Android會不斷的去監(jiān)控/dev/input/目錄下的所有的設(shè)備節(jié)點,一旦發(fā)現(xiàn)有新的設(shè)備節(jié)點可讀時就會立馬讀出事件并進(jìn)行處理禽最。
-
WMS
而這里的復(fù)雜步驟涉及到frameWork層腺怯,我們就從WMS開始吧,
先是有SystemServer啟動的WMS川无。SystemServer.java的startOtherServices()wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore);
并且同樣在這個方法中初始化了InputManagerService呛占,掌管輸入事件的服務(wù)。
inputManager = new InputManagerService(context);
我們看到
WindowManagerService
的main方法傳入的就是這個inputManager懦趋。
在InputManagerService的構(gòu)造方法中晾虑,用到了mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
native的方法,nativce層不是重點仅叫,我這邊就快速的將過去了帜篇。
在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
中(沒有在本地編譯過源碼的同學(xué)可以去 http://androidxref.com/ 查看,基于當(dāng)前最新的7.1.1)static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); eturn reinterpret_cast<jlong>(im); }
然后看內(nèi)部類nativeInputManger
NativeInputManager::NativeInputManager(jobject contextObj, ... sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
我們看到創(chuàng)建了一個EventHub類诫咱,并且將其交給InputManger并生成一個InputManger對象笙隙。
/frameworks/native/services/inputflinger/InputManager.cppInputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }
一個分發(fā)對象,一個reader對象坎缭,并且調(diào)用initialize方法
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
創(chuàng)建讀線程和分發(fā)線程
至此竟痰,所有的初始化先都o(jì)k了,在SystemServer.java掏呼,創(chuàng)建了InputManagerService之后沒幾行就調(diào)用了
inputManager.start();
坏快,public void start() { ... nativeStart(mPtr); ... }
又看到了native。憎夷。莽鸿。來吧繼續(xù)相當(dāng)枯燥的native,我要快進(jìn)了拾给,我有點寫的想吐祥得。臼予。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_t result = im->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } }
/frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputReader thread due to error %d.", result); mDispatcherThread->requestExit(); return result; } return OK; }
啟動了讀線程和分發(fā)線程
/frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; } void InputReader::loopOnce() { ... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); ... }
不斷的loop去通過EventHub去getEvents(越來越偏了,getEvents不繼續(xù)往下了啃沪,知道這個深度已經(jīng)對于非framework工程師來說已經(jīng)夠了)
在getEvents方法中去從dev/input/目錄下讀取設(shè)備節(jié)點并加工,并返回給InputReader進(jìn)行處理窄锅。之后的處理過程以及一系列跳轉(zhuǎn)也是相當(dāng)復(fù)雜创千,由于本文的初衷并非詳解最底層的東西, 故而此處一并略過直接到底層將event回傳給java層的最末
-
InputEventReceiver
在此我們只需要知道由InputChannel構(gòu)建起了UI進(jìn)程和底層system_server進(jìn)程的socket通道入偷。
最終會從NativeInputEventReceiver.cpp處調(diào)起InputEventReceiver的方法// Called from native code. @SuppressWarnings("unused") private void dispatchInputEvent(int seq, InputEvent event) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event); }
InputEventReceiver是個抽象類追驴,我們在ViewRootImpl中定義了如下
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(); } }
那么我們就看看enqueueInputEvent到底做了些什么操作:
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { ... if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } } void doProcessInputEvents() { while (mPendingInputEventHead != null) { ... deliverInputEvent(q); } ... } private void deliverInputEvent(QueuedInputEvent q) { ... if (stage != null) { stage.deliver(q); } else { finishInputEvent(q); } }
最終就在這個deliver方法中,而這個stage是個InputStage對象疏之,這個類內(nèi)部是鏈表結(jié)構(gòu)殿雪,最終會將q分發(fā)到可以處理的窗口ViewPostImeInputStage,由它的processPointerEvent方法來處理
private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; final View eventTarget = (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ? mCapturingView : mView; ... boolean handled = eventTarget.dispatchPointerEvent(event); ... return handled ? FINISH_HANDLED : FORWARD; }
調(diào)用的view的dispatchPointerEvent方法:
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
而我們知道,window的最底層的View就是DecorView锋爪,那么這個時候調(diào)用的應(yīng)該就是DecorView的dispatchTouchEvent方法
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
還記的callBack是誰么丙曙,在Activity的attach方法中,
mWindow.setCallback(this);
這個callback就是activity本身其骄,所以我們要去Activity中查看它的dispatchTouchEvent方法public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
這里分了兩步亏镰,先去getWindow().superDispatchTouchEvent(ev),這一步會從PhoneWindow->DecorView->ViewGroup(DecorView繼承FrameLayout就是ViewGroup),最后實際上是觸發(fā)了ViewGroup的dispatchTouchEvent方法拯爽,也就是activity會先將事件交給DecorView去處理索抓,如果被消耗掉,就返回true毯炮。如果沒有消耗這個事件逼肯,就回調(diào)Activity自己的onTouchEvent。
-
總結(jié)
-
那么把上面的一大坨我們簡略的來講如下的流程:
- 用戶觸摸屏幕產(chǎn)生設(shè)備節(jié)點中斷并保存到/dev/input/目錄下
- 底層的EventHub監(jiān)聽目錄桃煎,將事件讀出并加工返回給隨著WMS一起啟動的底層的InputReader
- InputReader處理加工之后交給InputDispatcher來進(jìn)行分發(fā)篮幢,通過socket通知UI進(jìn)程的InputEventReceiver接收到事件
- InputEventReceiver將回調(diào)事件一步步傳遞給Activity來進(jìn)行分發(fā)
- Activity先將事件交給DecorView來進(jìn)行處理,如果DecorView消耗則返回true为迈,否則自己回調(diào)onTouchEvent方法
以上洲拇!