最近在看Android觸摸屏事件相關的源碼,爲了對整個事件體系的瞭解,因此對事件相關,從事件的產生,寫入設備文件到咱們的應用層如何獲取到事件,整個傳遞機制源碼進行了分析,如下爲對於相關代碼的梳理過程當中的一些代碼剖析記錄。但願可以對你們有所幫助,同時也但願對於理解不當之處可以給予指正。android
針對事件的分析,這裏以觸摸屏事件爲例子,這也是咱們最經常使用的一個事件處理,這裏首先拋出咱們應用層相關使用代碼的例子,而後在來看事件管理的服務的啓動到如何讀取事件,事件的整個分發流程機制,如何一步步的走到咱們的應用層中,其中對於涉及到WMS,View,跨進程一些知識,這裏沒有具體深刻,接下來將針對每個知識點,進行相關的代碼分析。session
咱們平時的開發中,對於應用層的處理,主要在於爲View設置觸摸監聽器,而後從其中的回調函數中獲得咱們所須要的相關的觸摸的數據。咱們能夠對View進行設置相應的觸摸監聽器。app
@Override public boolean onTouch(View v, MotionEvent event) { float x = event.getX(); float y = event.getY(); return true; }
從這裏,咱們能夠獲取到觸摸事件的相關信息。那麼問題來了,這個事件是從哪裏傳遞來的呢?也就是說這個回調函數是被誰調用的呢?事件是如何傳遞到該View的。同時也涉及到了一個在應用層,事件的傳遞問題,當一個事件到達,是如何在View的父子層級中進行傳遞的。socket
對於Android Framework層的一些service,都是在SystemServer進程中建立的。和大多數的Service同樣,InputManager它也是在SystemServer中建立。在SystemServer的startOtherService中,進行了一些Service的建立。(對於SystemServer啓動相關在後續也會進行介紹們這裏先着於InputManagerService相關。)ide
InputManager inputManager = new InputManagerService(context); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start();
首先建立了一個InputManagerService的實例,而後將該服務加入到ServiceManger中,同時爲其設置了窗口的輸入監聽器,而後調用該服務的start方法。這裏咱們從其構造函數開始,而後再看一下它start方法的實現。函數
public InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); .... LocalServices.addService(InputManagerInternal.class, new LocalService()); }
首先建立了一個InputManagerHandler,同時傳入了DisplayThread的looper,這裏的InputManagerHandle爲InputManager的一個內部類,其中進行了一些消息的處理,調用native方法,nativeInit。其native實如今framework/services/core/jni下的com_android_server_input_InputManagerService.cppoop
其nativeInit的實現以下源碼分析
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); return reinterpret_cast<jlong>(im); }
獲取傳遞的消息隊列,而後根據其建立本地的NativeInputManager。ui
對於NativeInputManger實例的建立。其構造函數以下所示。this
NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); ... mInteractive = true; sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
建立了一個EventHub,同時利用EventHub來建立了一個InputManger實例。InputManger在framework/services/inputflinger下,
InputManger的構造函數代碼以下:
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }
經過構造函數,咱們能夠看到,這裏建立了一個InputDispatcher根據傳入的分發策略,而後建立了InputReader,傳遞了讀的策略和Dispatcher仍是有EventHub。接下來調用了initialize方法。
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
將上面建立的讀和分發事件的核心類傳入咱們所建立的線程之中。到此,在咱們的SystemServer中,對於InputManager的建立已經完成,接下來調用了InputManager的start方法,顧名思義,是對於這個InputManger服務的開始。代碼中的相關實現。
核心調用
nativeStart(mPtr);
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); status_t result = im->getInputManager()->start(); }
nativeStart函數中調用了InputManager的start方法。該方法執行以下
status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); }
至此,咱們能夠看到start方法所作的事情是啓動了在以前建立InputManger的時候,建立的DispatcherThread和ReaderThread.
上述建立啓動流程圖。
InputManager是系統事件處理的核心,它使用了兩個線程,一個是InputReaderThread,讀和處理未加工的輸入事件而後發送事件到由DispatcherThread管理的隊列中。InputDispatcherThread等待着在隊列上新來的事件,而後將它們分發給應用程序。而對於上層的類只是對這的一個包裝。因此要對輸入服務有個細緻的瞭解,對於InputManager類的剖析相當重要。
至此,咱們能夠看到InputManager開啓兩個線程,同時建立了Dispatcher核心類和InputReader核心類。
在NativeInputManager的構造函數中,建立了EventHub,同時將其做爲參數傳遞給InputManager,在InputManager構造函數中,建立InputRead的時候,傳遞了EventHub,EventHub的做用是未來源不一樣的各類信息,轉化成爲一種類型的信息,而後將這些信息提交到上層,給上層作處理。也就是說在輸入設備中,各類類型的輸入信息,經過EventHub進行一個處理以後,將信息轉化爲同一種類型的信息傳遞到上層。
在對源碼分析以前,這裏先講一下epoll,當咱們的應用程序要對多個輸入流進行監控的時候,處理多個輸入流來的數據,咱們能夠採起的一個方式是,對於這每個流進行遍歷,檢測到有數據,讀出。
while true { for i in stream[]; { if i has data read until unavailable } }
若是有數據,則讀取直到其中沒有數據爲止。該種方式也稱爲忙輪詢,可是當輸入流一直沒有數據的時候,就是在空消耗CPU,所以產生了select,poll,epoll。select和poll類似,其實現爲,當沒有數據的時候阻塞,一旦有了數據,經過輪詢的方式讀取數據。
while true { select(streams[]) for i in streams[] { if i has data read until unavailable } }
可是當咱們的流比較多的時候,對於輪詢檢測每個輸入流,也是比較消耗的,所以,epoll產生了,當沒有數據的時候,豈會阻塞,可是當有數據的時候,epoll可以把哪個流發生了怎樣的事件發送給咱們,這樣咱們對返回的流進行操做就是有意義的了。
先從EventHub的構造函數看起。
EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), mOpeningDevices(0), mClosingDevices(0), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); //建立一個epoll句柄 mEpollFd = epoll_create(EPOLL_SIZE_HINT); mINotifyFd = inotify_init(); //監視dev/input目錄的變化刪除和建立變化 int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = EPOLL_ID_INOTIFY; //把inotify的句柄加入到epoll監測 result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); //建立匿名管道 int wakeFds[2]; result = pipe(wakeFds); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; //將管道的讀寫端設置爲非阻塞 result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); eventItem.data.u32 = EPOLL_ID_WAKE; //將管道的讀端加入到epoll監測 result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); int major, minor; getLinuxRelease(&major, &minor); // EPOLLWAKEUP was introduced in kerel 3.5 mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5); }
構造函數首先建立了epoll句柄,而後建立了inotify句柄,而後建立了一個匿名管道,並將匿名管道設置爲非阻塞,inotify是Linux下的一個監控目錄和文件變化的機制,這裏監控了/dev/input目錄,當這個目錄發生變化,就代表有輸入設備加入或者移除。至此,EventHub只是進行了一些監控操做的處理。而對於EventHub相關事件處理部分的調用則是在建立ReaderThread的時候。
ReaderThread是繼承自Android的Thread實現。下面是一個建立Android中Native 線程的方式。
namespace android { class MyThread: public Thread { public: MyThread(); //virtual ~MyThread(); //若是返回true,循環調用此函數,返回false下一次不會再調用此函數 virtual bool threadLoop(); }; }
ReaderThread的threadLoop實現以下,因爲返回的結果爲true,因此這裏InputReader的loopOnce會被循環調用。
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }
下面是loopOnce實現的簡略版,只是抽離了其中對於事件的處理。
void InputReader::loopOnce() { ..... //從EventHub中獲取事件 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); //若是讀到數據,處理事件數據 if (count) { processEventsLocked(mEventBuffer, count); } if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { #if DEBUG_RAW_EVENTS ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); #endif mNextTimeout = LLONG_MAX; timeoutExpiredLocked(now); } } if (oldGeneration != mGeneration) { inputDevicesChanged = true; getInputDevicesLocked(inputDevices); } } // release lock //將排隊的事件隊列發送給監聽者,實際上這個監聽者就是Input dispatcher mQueuedListener->flush(); }
其中比較關鍵的幾個地方爲getEvents()
和processEventsLocked()
接下來讓對這個幾個重要的方法進行一一的分析。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { RawEvent* event = buffer; size_t capacity = bufferSize; for(;;) { .... while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; ..... ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); if (eventItem.events & EPOLLIN) { int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // 設備被移除,關閉設備 deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { //沒法得到事件 if (errno != EAGAIN && errno != EINTR) { ALOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { //得到事件的大小非事件類型整數倍 ALOGE("could not get event (wrong size: %d)", readSize); } else { int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; //計算讀入了多少事件 size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { struct input_event& iev = readBuffer[i]; if (iev.type == EV_MSC) { if (iev.code == MSC_ANDROID_TIME_SEC) { device->timestampOverrideSec = iev.value; continue; } else if (iev.code == MSC_ANDROID_TIME_USEC) { device->timestampOverrideUsec = iev.value; continue; } } //事件時間相關計算,時間的錯誤可能會致使ANR和一些bug。這裏採起一系列的防範 ......... event->deviceId = deviceId; event->type = iev.type; event->code = iev.code; event->value = iev.value; event += 1; capacity -= 1; } if (capacity == 0) { //每到咱們計算完一個事件,capacity就會減1,若是爲0。則表示 結果緩衝區已經滿了, //須要重置開始讀取時間的索引值,來讀取下一個事件迭代 mPendingEventIndex -= 1; break; } } //代表讀到事件了,跳出循環 if (event != buffer || awoken) { break; } mPendingEventIndex = 0; int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); if (pollResult == 0) { mPendingEventCount = 0; break; } //判斷是否有事件發生 if (pollResult < 0) { mPendingEventCount = 0; } else { //產生的事件的數目 mPendingEventCount = size_t(pollResult); } } //產生的事件數目 return event - buffer; }
方法開始部分會進行一些新增設備和移除設備的相關操做,來更新內部的狀態。這裏咱們聚焦於事件部分。來看一下,觸摸屏事件是如何被讀出的。這裏傳入了一個參數RawEvent* ,RawEvent的指針,經過這個指針做爲起始地址,記錄了咱們的事件,對於事件處理相關,是在一個循環體中,獲取時間,檢測事件對應的先關設備類型,而後讀取事件,爲事件進行賦值,若是有事件被讀到,直接跳出循環,若是沒有則會繼續等待直到事件的到來。同時更新mPendingEventCout和mPendingEventIndex,經過這兩個變量來控制事件的讀取,同時讀取的過程當中和開始也會進行一些設備增長或者移除的相關事件的處理。事件的來源則是經過epoll_wait獲得。而這個epoll也就是咱們開始的時候對一些目錄的輸入監聽。而事件的寫入則是由相關的設備驅動寫入,這裏對驅動相關再也不展開分析。
InputReaderThread函數不斷地調用looperOnce函數,不斷的從中讀取事件,那麼下一個問題來了,讀取到事件要放置到哪裏,又在哪裏被消耗掉了呢?也就是事件接下來的流向問題。讓咱們回到looperOnce以前。
在調用了getEvent以後,又調用了函數processEventsLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count;) { int32_t type = rawEvent->type; size_t batchSize = 1; if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { int32_t deviceId = rawEvent->deviceId; while (batchSize < count) { if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT || rawEvent[batchSize].deviceId != deviceId) { break; } batchSize += 1; } processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: removeDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::FINISHED_DEVICE_SCAN: handleConfigurationChangedLocked(rawEvent->when); break; default: ALOG_ASSERT(false); // can't happen break; } } count -= batchSize; rawEvent += batchSize; } }
首先對於事件類型進行了判斷,將事件中添加,移除設備的事件,和設備自身產生的事件進行了區分,這裏咱們只關心對於設備自身產生的事件。也就是processEventsForDeviceLocked
函數中所進行的操做。
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { ssize_t deviceIndex = mDevices.indexOfKey(deviceId); if (deviceIndex < 0) { ALOGW("Discarding event for unknown deviceId %d.", deviceId); return; } InputDevice* device = mDevices.valueAt(deviceIndex); if (device->isIgnored()) { //ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; } device->process(rawEvents, count); }
根據事件得到相應的設備類型,而後將事件交給相應的設備處理。
InputDevice的process方法
void InputDevice::process(const RawEvent* rawEvents, size_t count) { .... for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); } .... }
這裏的事件又交給了InputMapper來處理
InputMapper對應了不少的子類,這裏根據事件的類型進行相應的派發,處理。
事件到了這裏以後,如何傳遞到應用層,這裏mapper->process進行了那些處理。這裏來看一下對於觸摸屏事件的處理函數。
void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { sync(rawEvent->when); } }
經過這裏的函數處理,咱們繼續追蹤函數的數據流向。對於相關的事件處理,這裏最終執行的是將
void TouchInputMapper::sync(nsecs_t when) { ..... processRawTouches(false /*timeout*/); }
在相關的函數調用以後,最終調用了dispatchTouches
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { .... dispatchMotion(); .... }
對於相關事件的分發最終調用到了dispatchMotion(),對事件數據進行組裝以後,調用了
void TouchInputMapper::dispatchMotion() { .... NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, action, actionButton, flags, metaState, buttonState, edgeFlags, mViewport.displayId, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); getListener()->notifyMotion(&args); }
getListener函數
InputListenerInterface* InputReader::ContextImpl::getListener() { return mReader->mQueuedListener.get(); }
notifyMotion函數實現
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); }
這裏能夠看到,咱們將觸摸相關的事件進行包裝以後,將其加入到一個ArgsQueue隊列,到此,咱們已經將數據加入到參數隊列中,到此事件從設備文件獲取到寫入流程已經完成,這裏讓咱們再回到loopOnce方法中,最後調用了QueuedInputListener
的flush
方法,
void QueuedInputListener::flush() { size_t count = mArgsQueue.size(); for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; args->notify(mInnerListener); delete args; } mArgsQueue.clear(); }
NotifyArgs的notify函數實現
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyMotion(this); }
對於這個listener的建立來自於InputReader構建的時候。
mQueuedListener = new QueuedInputListener(listener);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
而這裏的Listener則是InputDispatcher
,InputDispatcher 的notifyMotion實現源碼。
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ..... MotionEvent event; event.initialize(args->deviceId, args->source, args->action, args->actionButton, args->flags, args->edgeFlags, args->metaState, args->buttonState, 0, 0, args->xPrecision, args->yPrecision, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); .... MotionEntry* newEntry = new MotionEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); .... if (needWake) { mLooper->wake(); } }
在該函數中,所作的事情是對於所傳遞的參數,構造MotionEntry,而後將其加入到enqueueInboundEventLocked之中。而後喚醒其中的looper。
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); ... //進行一些事件和窗口相關的判斷處理 }
Dispatcher開啓的線程中,每次循環的操做如何?
bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; }
Dispatcher下dispatchOnce的實現
void InputDispatcher::dispatchOnce() { ... dispatchOnceInnerLocked(&nextWakeupTime); ... }
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { .... mPendingEvent = mInboundQueue.dequeueAtHead(); .... switch (mPendingEvent->type) { case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { dropReason = DROP_REASON_APP_SWITCH; } if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEventLocked(currentTime, typedEntry)) { dropReason = DROP_REASON_STALE; } if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { dropReason = DROP_REASON_BLOCKED; } done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } .... } }
從mInboudQueue中,獲取到事件,而後對事件類型進行判斷,判斷以後調用了dispatchMotionLocked函數,來繼續進行事件的傳遞。
dispatchEventLocked,
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { .... pokeUserActivityLocked(eventEntry); ..... for (size_t i = 0; i < inputTargets.size(); i++) { const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } } }
得到目標輸入,根據InputChannel獲取相應的鏈接,而後調用prepareDispatchCycleLocked(),進行事件的派發。enqueueDispatchEntriesLocked,在該方法中又調用了startDispatchCycleLocked方法。其實現爲
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { EventEntry* eventEntry = dispatchEntry->eventEntry; .... switch (eventEntry->type) { .... case EventEntry::TYPE_MOTION: { status = connection->inputPublisher.publishMotionEvent( ....); break; } .... } ... }
至此調用了connection 的inputPublisher的publishMotionEvent方法將事件分發消耗。
InputPublisher定義在InputTransport.cpp中,
status_t InputPublisher::publishMotionEvent(...) { .... InputMessage msg; msg.header.type = InputMessage::TYPE_MOTION; msg.body.motion.seq = seq; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; msg.body.motion.edgeFlags = edgeFlags; msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } return mChannel->sendMessage(&msg); }
該方法所執行的操做是利用傳入的觸摸信息,構建點擊消息,而後經過InputChannel將消息發送出去。這裏引出了InputChannel,在此,咱們通InputPublisher的建立反推出InputChannel是什麼時候被引入的,什麼時候被建立的。從而進一步分析其做用。在分析以前先讓咱們來對上述的分析過程作一個總結。
ReaderThread開啓後會從EventHub中輪詢獲取時間,獲取到事件以後,對手將進行一系列的處理,最終將通過一系列處理封裝的事件信息經過InputChannel發送出去。
到此,對於輸入事件,咱們已經分析到了InputChannel,對於其上的具體分析轉化,將是接下來分析的核心。
從上面分析能夠看到事件傳遞部分最後是經過InputChannel所發送出去的,那麼InputChannel是在什麼時候被建立的呢?什麼時候被InputManager所使用的呢?
public void registerInputChannel(InputChannel inputChannel, InputWindowHandle inputWindowHandle) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null."); } nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false); }
nativeRegisterInputManger
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { throwInputChannelNotInitialized(env); return; } sp<InputWindowHandle> inputWindowHandle = android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); status_t status = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor); if (status) { String8 message; message.appendFormat("Failed to register input channel. status=%d", status); jniThrowRuntimeException(env, message.string()); return; } if (! monitor) { android_view_InputChannel_setDisposeCallback(env, inputChannelObj, handleInputChannelDisposed, im); } }
NativeInputManager的registerInputChannel
還會調用到InputDispatcher的registerInputChannel,會經過InputChannel建立相應的Connection,同時將InputChannel加入到相應的監控之中。在上面對代碼的分析之中,獲取InputChannel,就是經過這個Connection來獲取的。
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { { // acquire lock AutoMutex _l(mLock); if (getConnectionIndexLocked(inputChannel) >= 0) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().string()); return BAD_VALUE; } sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); if (monitor) { mMonitoringChannels.push(inputChannel); } mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock // Wake the looper because some connections have changed. mLooper->wake(); return OK; }
那麼這個InputChannel是在什麼時候建立的呢?同時,對於InputReaderThread和InputDispatcherThread是運行在SystemServer進程中的,而咱們的應用進程是和其不在同一個進程中的。這之間必定也是有進程間的通訊機制在裏面。
InputChannel的建立是在 ViewRootImpl
中setView
方法中。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { .... if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } .... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); .... }
這裏對於ViewRootImpl和WindowSession相關暫且不介紹,對於這方面的知識,須要很大的篇幅來介紹,這裏先只是講到是在這裏建立的,對於其相關的內容將在後續的文章中介紹。這裏首先是建立了一個InputChannel,而後將其調用了WindowSession
的addToDisplay
方法將其做爲參數傳遞。
public InputChannel() { }
在InputChannel中的方法都爲調用了相應的native方法。這裏調用的addToDisplay將會把InputChannel添加到WindowManagerService中。會調用WMS的addWindow
方法。
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { .... final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); if (openInputChannels) { win.openInputChannel(outInputChannel); } .... }
對於InputChannel的相關處理交給了WindowState的openInputChannel方法。
void openInputChannel(InputChannel outInputChannel) { if (mInputChannel != null) { throw new IllegalStateException("Window already has an input channel."); } String name = makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); mInputChannel = inputChannels[0]; mClientChannel = inputChannels[1]; mInputWindowHandle.inputChannel = inputChannels[0]; if (outInputChannel != null) { mClientChannel.transferTo(outInputChannel); mClientChannel.dispose(); mClientChannel = null; } else { mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel); } mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle); }
首先調用了InputChannel的openInputChannelPair
方法,該方法調用了InputChannel的native方法nativeOpenInputChannelPair
,建立了兩個InputChannel
,對其中一個經過InputManager
進行了InputChannel的註冊。對於InputChannel
的相關Native的實現是在InputTransport中,nativeOpenInputChannelPair
的源碼以下。
status_t InputChannel::openInputChannelPair(const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.string(), errno); outServerChannel.clear(); outClientChannel.clear(); return result; } 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)); String8 serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; }
這裏建立了兩個Socket,這兩個Socket設置具有讀寫雙端,而後根據建立的socket,建立出兩個InputChannel,一個Server,一個Client。這樣在SystemServer進程和應用進程間的InputChannel的通訊就能夠經過這邊,因爲兩個channel不在同一個進程中,這裏進程通訊則是經過其中的socket來進行。在sendMessage
和receiveMessage
中,經過對Socket的寫,讀操做來實現消息的傳遞。
status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; do { nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) { return DEAD_OBJECT; } return -error; } if (size_t(nWrite) != msgLength) { return DEAD_OBJECT; } return OK; }
接收消息,經過讀socket的方式來讀取消息。
status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) { return DEAD_OBJECT; } return -error; } if (nRead == 0) { // check for EOF return DEAD_OBJECT; } if (!msg->isValid(nRead)) { return BAD_VALUE; } return OK; }
接收端的消息由誰來觸發呢?是如何觸發開始接受消息,消息如何在傳到InputChannel以後,進行的進一步的數據傳遞呢?這是接下來所要去分析的,這裏先對上面InputChannel進行一個總結。
以前的setView
中,咱們建立了InputChannel以後,開啓了對於InputChannel中輸入事件的監聽。
if (mInputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); }
WindowInputEventReceiver的構造函數以下,其繼承自InputEventReceiver。
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } .... }
InputEventReceiver的構造函數源碼以下
public InputEventReceiver(InputChannel inputChannel, Looper looper) { .... mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), inputChannel, mMessageQueue); }
這裏調用了native方法來作初始化,相關的native方法的實如今android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { .... sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue); status_t status = receiver->initialize(); ..... }
根據傳入的InputChannel
和MessageQueue
,建立一個NativeInputEventReceiver,而後調用其initialize
方法。
status_t NativeInputEventReceiver::initialize() { setFdEvents(ALOOPER_EVENT_INPUT); return OK; }
在initialize()
方法中,只調用了一個函數setFdEvents
,
void NativeInputEventReceiver::setFdEvents(int events) { if (mFdEvents != events) { mFdEvents = events; int fd = mInputConsumer.getChannel()->getFd(); if (events) { mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL); } else { mMessageQueue->getLooper()->removeFd(fd); } } }
從InputConsumer中獲取到channel的fd,而後調用Looper的addFd
方法。
int ALooper_addFd(ALooper* looper, int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { return ALooper_to_Looper(looper)->addFd(fd, ident, events, callback, data); }
Looper的addFd的實現以下
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { Request request; request.fd = fd; request.ident = ident; request.events = events; request.seq = mNextRequestSeq++; request.callback = callback; request.data = data; if (mNextRequestSeq == -1) mNextRequestSeq = 0; struct epoll_event eventItem; request.initEventItem(&eventItem); ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { return -1; } mRequests.add(fd, request); } }
該方法所執行的操做就是對傳遞的fd添加epoll監控,Looper會循環調用pollOnce
方法,而pollOnce
方法的核心實現就是pollInner
。其代碼大體實現內容爲等待消息的到來,當有消息到來後,根據消息類型作一些判斷處理,而後調用其相關的callback。咱們當前是對於開啓的socket的一個監聽,當有數據到來,咱們便會執行相應的回調。這裏對於InputChannel的回調是在調用了NativeInputEventReceiver的handleEvent
方法。
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; } .... return 1; }
對於Event的處理,這裏調用consumeEvents來對事件進行處理。
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { ... for(;;) { ... InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); ... } ... }
InputConsumer是在InputTransport中作的聲明。
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { while (!*outEvent) { .... status_t result = mChannel->receiveMessage(&mMsg); .... } }
調用consume方法會持續的調用InputChannel的receiveMessage方法來從socket中讀取數據。到這裏,咱們已經將寫入socket的事件讀出來了。
事件在從socket讀出以後,通過傳遞,最終會調用到ViewRootImpl的enqueueInputEvent
方法。
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { adjustInputEventForCompatibility(event); QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); QueuedInputEvent last = mPendingInputEventTail; if (last == null) { mPendingInputEventHead = q; mPendingInputEventTail = q; } else { last.mNext = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1; if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }
enqueueInputEvent方法從InputEventReceiver中獲取到InputEvent,而後將其加入到當前的事件隊列之中,最後調用doProcessInputEvents
來進行處理。
void doProcessInputEvents() { while (mPendingInputEventHead != null) { QueuedInputEvent q = mPendingInputEventHead; mPendingInputEventHead = q.mNext; if (mPendingInputEventHead == null) { mPendingInputEventTail = null; } q.mNext = null; mPendingInputEventCount -= 1; 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); } // 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; mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); } }
遍歷全部的消息,若是事件類型爲觸摸屏事件,對其進行相應的時間修改,最後對於每個處理完成的事件調用deliverInputEvent
,
private void deliverInputEvent(QueuedInputEvent q) { q.mEvent.getSequenceNumber()); 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); } }
在事件分發環節,首先進行事件的一個判斷,經過shouldSkipIme來判斷是否傳遞給輸入法,而後決定使用何種InputStage進行消息的繼續傳遞,這裏實現了多種InputStage,對於每個類型的InputStage都實現了一個方法process
方法來針對不一樣類型的事件作處理,若是是觸摸屏類的消息,最終會將事件的處理轉交到View的身上。對於InputStage涉及的篇幅較多,這裏也再也不展開,當消息到達ViewRootImpl中後,接下來就是在View間的派發。
對於View層的事件派發,咱們最多見的就是dispatchTouchEvent
,onTouch
,onInterceptTouchEvent
,onClick
,onTouchEvent
等。對於View樹上事件的派發,就是在對樹的遍歷傳遞中,主要起做用的就是這幾個函數。這裏咱們先從View的相關事件函數開始分析,因爲ViewGroup具備子View的緣由,其相關的事件派發邏輯和View有所區別,這裏咱們先進行View的事件分析。
public boolean dispatchTouchEvent(MotionEvent event) { .... 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上的事件派發,核心操做是兩點,一個是調用監聽器的onTouch方法,而後判斷事件是否被消耗,若是沒有被消耗,則會調用onTouchEvent方法。在onTouchEvent中根據消息類型進行一些處理。
根據事件類型來更新內部的一些狀態。這裏比較複雜的仍是在ViewGroup中的事件分發邏輯,這裏在分發的過程當中,須要判斷是否對事件進行攔截,若是不攔截,是否自身可處理,若是須要考慮到其中的子View。這裏對其中的關鍵代碼進行逐步分析。
final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { intercepted = true; }
判斷是否進行攔截,調用自身的onInterceptTouchEvent
,開發者能夠重載這個方法進行本身的一些操做。返回true表示攔截事件。若是要對事件進行攔截,則再也不進行子View的遍歷。不然將會進行子View的遍歷,事件傳遞,在子View的事件傳遞結束以後,若是子View將事件消耗了則會將其加入到mFirstTouchTarget,若是遍歷完成沒有任何被添加
if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); }
接下來進行的是對於事件在子View中的派發,這裏咱們也只是針對其中的核心代碼進行分析。
final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); .... dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign); .... }
在對子View進行遍歷的處理的時候,若是子View的事件被消耗,那麼咱們就會將其TouchTarget賦值給mFirstTouchTarget
,當檢測到mFirstTouchTarget爲空時會再調用Viewgroup自身的dispatchTransformedTouchEvent方法,這個時候就會調用其onTouchEvent,而後繼續View中的事件傳遞流程。
if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); }
對於dispatchTransformedTouchEvent
函數
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } .... }
這裏事件的派發也是在調用了每個子View的dispatchTouchEvent方法,根據返回結果來判斷是否被消耗,一旦事件被消耗則會中止傳遞。
至此對於從硬件設備產生數據,到數據被逐層傳遞到應用程序中的整個流程就梳理完了。事件相關的建立,傳遞流程以下所示。