Android 是基於「事件驅動」模型的。所謂事件驅動,簡單地說就是你點什麼按鈕(即產生什麼事件),系統執行什麼操做(即調用什麼函數)。固然事件不只限於用戶的操做,事件驅動的核心天然是事件。java
從事件角度說,事件驅動程序的基本結構是由一個事件收集器、一個事件發送器和一個事件處理器組成。事件收集器專門負責收集全部事件,包括來自用戶的(如鼠標、鍵盤事件等)、來自硬件的(如時鐘事件等)和來自軟件的(如操做系統、應用程序自己等)。android
從廣義上來講,事件的發生源能夠分爲軟件
和硬件
兩類,這裏側重對硬件
部分的討論。c++
事件是由真實物理硬件產生的消息,代表設備使用者的某種意願。markdown
若是從硬件設備角度爲事件分類,最重要的有如下幾種分類app
按鍵事件 (KeyEvent)socket
由物理按鍵產生的事件。async
觸摸事件 (TouchEvent)ide
在觸摸屏上的點擊、滑動等事件,是 Android 系統中使用最普遍的事件。函數
鼠標事件 (MouseEvent)oop
鼠標操做引發的事件,如單擊、雙擊、移動等。
Android 針對上面提到的事件,提取出了一個統一的抽象接口,這就是 InputEvent 。
InputEvent 下面有兩個類:KeyEvent 和 MotionEvent。KeyEvent 用於表達按鍵事件,MotionEvent 則是將因此能產生 MoveEvent 的事件源進行統一管理。
事件源產生後,Linux 內核收集到原始信息,Android 系統經過 /dev/input 下的節點來訪問當前的發生的事件並對 原始數據 進行提煉,而後交由 WMS 去分配,WMS 做爲 InputEvent 的派發者,將事件派發到指定的 window 去處理,當事件到達應用程序端的時候,就已經變成相對 可理解、好處理 的了。
接下來咱們從 InputManagerService 入手,看看 InputEvent 事件是如何一步步變得好處理的。
InputManagerService 是由 SystemServer 統一啓動
/*framework/base/services/java/com/android/server/SystemServer.java */ private void startOtherServices() { ... inputManager = new InputManagerService(context); wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); inputManager.start(); 複製代碼
從上面能夠看出,InputManagerService 的實例直接做爲 WindowManagerService 的參數傳入,另外, IMS 也將本身注入到了 ServiceManager 中。最後調用 inputManager.start()
啓動 IMS。
/*framework/base/services/java/com/android/server/input/InputManagerService.java */ public void start() { nativeStart(mPtr); // mPtr 是 IMS 在構造過程當中調用 nativeInit 所建立的 NativeInputManagerService 對象 Watchdog.getInstance().addMonitor(this); // 將 IMS 加入監控體系 /* 接下來是一些監聽事件處理,這裏的實現包含大量的 native 調用 */ } 複製代碼
看來主要的事情都是 NativeInputManagerService 來處理的,接下來看看 NativeInputManagerService 的實現
/*framework/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/ static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == nullptr) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im); } // nativeInit 初始化了 NativeInputManager 對象 static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_t result = im->getInputManager()->start(); // getInputManager 返回的是 InputManager 對象 if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } } 複製代碼
nativeStart 最終實現的是 InputManager.start()
方法, InputManager 爲 IMS 建立了2 個線程,分別是 InputReaderThread
和InputDispatcherThread
,其中InputReaderThread
負責從從驅動節點讀取 Event,InputDispatcherThread
專職分發。咱們重點關注 Dispatcher 的部分,對於 Reader 部分這裏不作細說。
/*framework/native/services/inputflinger/InputManager.cpp*/ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); mReader = createInputReader(readerPolicy, mClassifier); // mReader 與 mDispatcher 在這裏創建了關係,如此 Reader線程 就有了通道將事件告知 Dispatcher 線程去執行分發流程 initialize(); } void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); //mDispatcherThread 的核心實現主要由 InputDispatcher 來實現 } 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; } 複製代碼
接下來主要關注 InputDispatcher 的分發部分,InputDispatcher 將它獲知到的系統事件使用 EventEntry 結構表示,能夠分爲如下幾類
enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; 複製代碼
本文討論的事件主要是這裏的 TYPE_KEY 類型;InputDispatcher 分發事件的核心函數爲InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime)
,其中針對 TYPE_KEY
類型執行的函數爲done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)
/*framework/native/services/inputflinger/InputDispatcher.cpp*/ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { .... 省略了前面一些前期工做與攔截處理 std::vector<InputTarget> inputTargets; int32_t injectionResult = ***findFocusedWindowTargetsLocked***(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } setInjectionResult(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } // Add monitor channels from event's or focused display. addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // 分發事件到目標中去 dispatchEventLocked(currentTime, entry, inputTargets); return true; } 複製代碼
dispatchKeyLocked 先經過 findFocusedWindowTargetsLocked() 來尋找目標窗口,成功找到後會將其加入 inputTargets 中,而且將 injectionResult 賦值爲 INPUT_EVENT_INJECTION_SUCCEEDED ;最終經過dispatchEventLocked將事件分發給目標窗口。
findFocusedWindowTargetsLocked() 能拿到目標窗口,那這裏面確定少不了 WMS 的參與,由於 WMS 纔是真正 window 的管家。還記得最開始,IMS 與 WMS 綁定的那些代碼嗎?具體的講,WMS 內部 持有一個 InputManagerCallback 的回調 mInputManagerCallback,IMS 又經過 setWindowManagerCallbacks() 函數將 mInputManagerCallback 做爲入參傳入,這InputManagerCallback 定義了一系列與事件相關的接口,當 Window 變更的時候,WMS 都會經過 InputManagerCallback 來與 IMS 交流。更細點來講,WMS 到 InputDispatcher 的過程當中使用到了 InputMonitor 這個"中介",感興趣的能夠自行去研究。
dispatchEventLocked() 這個函數負責將事件通知給應用程序去處理的, InputDispatcher與 InputTarget 通訊是經過 InputChannel 來進行的, InputChannel 是經過 socket 來進行通訊的。具體流程是 WMS 在調用 addWindow()
方法時候通會經過 InputChannel.openInputChannelPair(name)
方法打開一個雙向通道,而這個openInputChannelPair (源碼路徑/* framework/native/libs/input/InputTransport.cpp */) 在 native 層的實現就是經過 socket 實現的。
經過上面的分析,咱們對 native 層的事件與 WMS 的傳遞大體有了瞭解,而咱們更爲關心的則是應用層的事件傳遞。咱們知道,Activity 調用addView()
將 Window 添加到 WMS 的過程當中會使用到 ViewRootImpl 這個中介,咱們就從 ViewRootImpl 開始分析這一流程。
WMS 與 View 溝通的橋樑是經過 ViewRootImpl 來實現的。事件能回傳給 View 的前提是 View 所在的 Window 添加到了 WMS 中,而 View 添加到 WMS 的過程必然會走到 ViewRootImpl.setView()
方法,那麼就從這個方法開始跟蹤。
/*framework/base/core/java/android/view/ViewRootImpl.java*/ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ... if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets); catch (RemoteException e) { mInputChannel = null; }finally { ... } if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } // WindowInputEventReceiver 繼承了 InputEventReceiver mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix); InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, "aq:native-pre-ime:" + counterSuffix); mFirstInputStage = nativePreImeStage; mFirstPostImeInputStage = earlyPostImeStage; 複製代碼
若是 Window 支持事件響應,會 new 一個 InputChannel 對象,而後經過 WindowSession.addToDisplay()
將其做爲參數傳入 WMS ,以後經過 InputEventReceiver 來監聽 InputChannel 的回傳數據。結合前面 dispatchEventLocked() 中提到的 InputChannel 充當的角色,不難理解,InputEventReceiver 最終能收到 native 層的回傳消息。
以後以責任鏈的形式封裝了 InputStage,默認狀況下責任鏈的起點是 NativePreImeInputStage,終點爲 SyntheticInputStage,(固然這不是絕對的,mSyntheticInputStage、mFirstInputStage、mFirstPostImeInputStage 這三個參數就是來肯定責任鏈真正的起點的)針對上層 View 的分發邏輯在 ViewPostImeInputStage 中。根據這點,不難猜想到 WindowInputEventReceiver 在處理分發的過程當中會將事件分發給這個 InputStage 的責任鏈去處理。實際流程也是如此。歸納來講就是,WindowInputEventReceiver 收到事件後,WindowInputEventReceiver.onInputEvent()
開始執行,它的職責是將已經消費的事件通知底層,將未消費的事件經過 enqueueInputEvent()
加入隊,enqueueInputEvent()
中的doProcessInputEvents()
會不斷獲取當前須要處理的 InputEvent 而且將事件分發給 InputStage 去處理。當 Event 被 InputStage 處理完畢後,最終將處理結果經過InputEventReceiver.finishInputEvent()
通知 native 層。
/*framework/base/core/java/android/view/ViewRootImpl.java*/ private void deliverInputEvent(QueuedInputEvent q) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", q.mEvent.getSequenceNumber()); if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); } // 這裏肯定分發責任鏈的起點 InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } // 對 KeyEvent 的額外處理 if (q.mEvent instanceof KeyEvent) { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); // 將事件分發給指定 InputStage 去處理 } else { finishInputEvent(q); } } /** * Delivers an event to be processed. */ public final void deliver(QueuedInputEvent q) { if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (shouldDropInputEvent(q)) { finish(q, false); } else { apply(q, onProcess(q)); // 這裏執行 InputStage 的 onProcess 方法 } } 複製代碼
接下來重點關注ViewPostImeInputStage.onProcess()
方法,對其餘 InputStage 感興趣的同窗能夠經過翻閱源碼自行閱讀,流程都差很少的。從 processGenericMotionEvent 中能夠看出,事件又被分發給了 View Tree 的根元素 mView ( 大部分狀況下是Activity )去處理,如此一來 變開啓了 View 的事件分發流程。
/*framework/base/core/java/android/view/ViewRootImpl$ViewPostImeInputStage.java*/ protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); //若是是 KeyEvent 走 KeyEvent 的處理流程 } else { final int source = q.mEvent.getSource(); // 獲取事件源類型 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); // MotionEvent 類型的事件會執行到這來 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q); // 軌跡球事件類型 } else { return processGenericMotionEvent(q); // Motion 事件 } } } private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; // 將事件傳遞給 view 的 dispatchPointerEvent 處理 boolean handled = mView.dispatchPointerEvent(event); return handled ? FINISH_HANDLED : FORWARD; } 複製代碼
View 中處理的事件主要爲 TouchEvent ,它能夠細分爲如下幾個重要類型:
ACTION_DOWN
當手勢按下就會觸發,是後續一切事件的起點,通常會在這裏作一些狀態初始化工做。
ACTION_MOVE
當手勢按下並拖動,就會產生 ACTION_MOVE 事件。
ACTION_UP
ACTION_UP 是手勢操做的結束點。這裏經過與 ACTION_DOWN 時候設置的參數進行比較就能夠判斷是否爲長按事件等。
ACTION_CANCEL
ACTION_CANCEL 事件不禁用戶主動產生,而是系統經過判斷後得出的結果。能夠簡單看作是手勢結束的標識,處理相似於 ACTION_UP,並作好清理工做。
/*framework/base/core/java/android/view/View.java */ public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } } public boolean dispatchTouchEvent(MotionEvent event) { boolean result = false; if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } return result; } 複製代碼
View 中分發 TouchEvent 仍是比較簡單的,主要考慮到了兩個因素:
onTouch
View 對象能夠經過 setOnTouchListener 來設置一個 Event 的監聽者,對應上面的 li.mOnTouchListener ,這樣當事件來臨時,View 會主動調用這個監聽者的 onTouch 方法去消費事件。
onTouchEvent
用戶沒有指定 TouchListener ,或者 flag 指定爲 disable, 亦或是用戶指定了 TouchListener 可是 TouchListener.onTouch() 返回值爲 false 的狀況下,系統纔會將 event 傳遞給 onTouchEvent 。
ViewGroup 的 TouchEvent 投遞流程相對複雜一些,由於涉及到對子對象的處理,多了一個攔截的概念。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // ACTION_DOWN 事件來臨,先清除掉以前的全部狀態 cancelAndClearTouchTargets(ev); resetTouchState(); } // 檢查攔截狀況 final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // 若是disallowIntercept 爲 false,調用 onInterceptTouchEvent 方法 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; //不攔截 } } else { intercepted = true; //攔截 } // 檢查 CANCEL 的狀況 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { //不攔截的狀況 if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); for (int i = childrenCount - 1; i >= 0; i--) { // 省略了循環查找一個能處理此事件的 child 的過程 if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. ... break; } .... return handled } 複製代碼
ViewGroup 的 dispatchTouchEvent 首先會檢查是否被攔截,用intercepte來表示,若是 intercepted 爲 false,代表 ViewGroup 不但願攔截這一消息,於是它的子對象纔有機會來處理它。若是 intercepted 爲 true,說明 ViewGroup 須要攔截這個事件本身來處理,以後經過 dispatchTransformedTouchEvent()
的super.dispatchTouchEvent()
觸發 onTouchEvent()
回調。
intercepted 要爲 true 須要知足兩個條件:
disallowIntercept 爲 false
能夠經過調用 ViewParent 接口的 requestDisallowInterceptTouchEvent(true) 使 disallowIntercept 爲false,Android 中僅 ViewGroup 和ViewRootImpl 繼承了 ViewParent 接口,而 ViewRootImpl 中的 requestDisallowInterceptTouchEvent 是一個空實現。
也就是說,只有 ViewGroup.requestDisallowInterceptTouchEvent(true) 有效。
onInterceptTouchEvent() 返回值爲 true
onInterceptTouchEvent() 能夠體現出每一個 ViewGroup 「差別化」的地方,這個方法是 Android 鼓勵你們在繼承 ViewGroup 時候重載的方法。
canReceivePointerEvents()
用於判斷當前 child 是否能接收 PointerEvents,dispatchTransformedTouchEvent()
則計算(x,y)這個點有沒有落在此 child 區域; 若是 child 不符合要求,直接跳過,能夠節省時間。若是找到了符合要求的 child,就經過 dispatchTransformedTouchEvent()
來調用child.dispatchTouhEvent()
,這個 child 有多是 View,也有多是 ViewGroup。
Activity 的 dispatchTouchEvent 首先會將事件交給它的 window 對象去處理,Activity 的 getWindow() 獲得的 Window 對象是 PhoneWindow,PhoneWindow 的 superDispatchTouchEvent() 方法又將事件分發給 mDecor 去處理,這個 mDecor 是一個 FrameLayout,是在 Activity 調用 setContentView() 時生成的。具體的流程這裏再也不展開,對這塊有疑問的同窗能夠自行跟蹤 setContentView() 的調用流程。FrameLayout 又是繼承 ViewGroup 的,如此便和上面 View 的 dispatchTouchEvent 創建了關聯。
/*framework/base/core/java/android/app/Activity.java*/ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); } 複製代碼
/*framework/base/core/java/com/android/internal/policy/PhoneWindow.java*/ @Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); } 複製代碼