編輯:關於Android編程
vcPmvavKwrz+zai5/UlucHV0TWFuYWdlcqOsSW5wdXRNb25pdG9y0rvCt7SruPhQaG9uZVdpbmRvd01hbmFnZXLAtNf2z7XNs8rkyOvKwrz+tcS0psDto6zB7dK7t73D5r2r1eLQqcrCvP60q7j4vbm147ywvODK07Swv9qho05hdGl2ZUlucHV0TWFuYWdlcsq1z9ZJbnB1dFJlYWRlclBvbGljeUludGVyZmFjZbrNSW5wdXREaXNwYXRjaGVyUG9saWN5SW50ZXJmYWNlvdO/2qOs1NpOYXRpdmWy47XESW5wdXRNYW5hZ2Vyus1KYXZhsuO1xElNU7zkxvC1vdK7uPa9usuusuO1xNf308Oho0lucHV0TW9uaXRvcsq1z9bBy1dpbmRvd01hbmFnZXJDYWxsYmFja3O907/ao6zG8LW9wctJTVO1vVdNU7XEway909f308Oho0FwcNXisd+jrFZpZXdSb290SW1wbM/gtbHT2kFwcLbL0ru49ralsuNWaWV3tcRDb250cm9sbGVyoaPV4rj2tqWy41ZpZXfU2ldNU9bQttTTptK7uPa0sL/ao6zTw1dpbmRvd1N0YXRlw+jK9qGjV2luZG93U3RhdGXW0NPQSW5wdXRXaW5kb3dIYW5kbGW0+rHt0ru49r3TytXK5MjrysK8/rXEtLC/2r7ksfqho0lucHV0RGlzcGF0Y2hlctbQtcRtRm9jdXNlZFdpbmRvd0hhbmRsZda4yr7By725teO0sL/atcS+5LH6oaNJbnB1dERpc3BhdGNoZXK53MDtwcvSu9vnway906Oo0ru49sGsvdO21NOm0ru49teisuG1vVdNU7XEtLC/2qOpo6zNqLn91eLQqbj2way900lucHV0RGlzcGF0Y2hlcr/J0tTWsb3TvavK5MjrysK8/reizflBcHC2y7XEvbm147Swv9qho8rkyOvKwrz+tNNEcml2ZXK/qsq8tcS0psDtuf2zzLTz1sLI58/Co7o8YnI+CjwvcD4KPHA+PGltZyBzcmM9"/uploadfile/Collfiles/20141215/2014121508324848.png" alt="\">
為了故事的完整性,還是先看下初始化。SystemServer中初始化IMS,然後初始化WMS,把IMS作為參數傳入。
470 Slog.i(TAG, "Input Manager"); 471 inputManager = new InputManagerService(context); 472 473 Slog.i(TAG, "Window Manager"); 474 wm = WindowManagerService.main(context, inputManager, 475 mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, 476 !mFirstBoot, mOnlyCore); ... 483 inputManager.start();
可以看到,初始化時依次初始化NativeInputManager,EventHub,InputManager, InputDispatcher,InputReader,InputReaderThread, InputDispatcherThread。NativeInputManager可看作IMS和InputManager的中間層,將IMS的請求轉化為對InputManager及其內部對象的操作,同時將InputManager中模塊的請求通過JNI調回IMS。InputManager是輸入控制中心,它有兩個關鍵線程InputReaderThread和InputDispatcherThread,它們的主要功能部分分別在InputReader和InputDispacher。前者用於從設備中讀取事件,後者將事件分發給目標窗口。EventHub是輸入設備的控制中心,它直接與input
driver打交道。負責處理輸入設備的增減,查詢,輸入事件的處理並向上層提供getEvents()接口接收事件。在它的構造函數中,主要做三件事:
1. 創建epoll對象,之後就可以把各輸入設備的fd掛在上面多路等待輸入事件。
2. 建立用於喚醒的pipe,把讀端掛到epoll上,以後如果有設備參數的變化需要處理,而getEvents()又阻塞在設備上,就可以調用wake()在pipe的寫端寫入,就可以讓線程從等待中返回。
3. 利用inotify機制監聽/dev/input目錄下的變更,如有則意味著設備的變化,需要處理。
因為事件的處理是流水線,需要InputReader先讀事件,然後InputDispatcher才能進一步處理和分發。因此InputDispatcher需要監聽InputReader。這裡使用了Listener模式,InputDispacher作為InputReader構造函數的第三個參數,它實現InputListenerInterface接口。到了InputReader的構造函數中,將之包裝成QueuedInputListener。QueuedInputListener中的成員變量mArgsQueue是一個緩沖隊列,只有在flush()時,才會一次性通知InputDispatcher。QueuedInputListener應用了Command模式,它通過包裝InputDispatcher(實現InputListenerInterface接口),將事件的處理請求封裝成NotifyArgs,使其有了緩沖執行的功能。
全初始化好後,SystemServer調用start()函數讓InputManager中兩個線程開始運行。先看InputReaderThread,它是事件在用戶態處理過程的起點。這裡以按鍵事件的處理為例。
InputReaderThread不斷調用InputReader的pollOnce()->getEvents()函數來得到事件,這些事件可以是輸入事件,也可以是由inotify監測到設備增減變更所觸發的事件。第一次進入時會掃描/dev/input目錄建立設備列表,存在mDevice成員變量中(EventHub中有設備列表KeyedVector
155void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { 156 mArgsQueue.push(new NotifyKeyArgs(*args)); 157}在InputReader的loopOnce()的結尾會調用QueuedInputListener::flush()統一回調緩沖隊列中各元素的notify()接口:
171void QueuedInputListener::flush() { 172 size_t count = mArgsQueue.size(); 173 for (size_t i = 0; i < count; i++) { 174 NotifyArgs* args = mArgsQueue[i]; 175 args->notify(mInnerListener); 176 delete args; 177 } 178 mArgsQueue.clear(); 179}
以按鍵事件為例,最後會調用到InputDispatcher的notifyKey()函數中。這裡先將參數封裝成KeyEvent:
2416 KeyEvent event; 2417 event.initialize(args->deviceId, args->source, args->action, 2418 flags, keyCode, args->scanCode, metaState, 0, 2419 args->downTime, args->eventTime);然後把它作為參數調用NativeInputManager的interceptKeyBeforeQueueing()函數。顧名思義,就是在放到待處理隊列前看看是不是需要系統處理的系統按鍵,它會通過JNI調回Java世界,最終調到PhoneWindowManager的interceptKeyBeforeQueueing()。然後,基於輸入事件信息創建KeyEntry對象,調用enqueueInboundEventLocked()將之放入隊列等待InputDiaptcherThread線程拿出處理。
2439 KeyEntry* newEntry = new KeyEntry(args->eventTime, 2440 args->deviceId, args->source, policyFlags, 2441 args->action, flags, keyCode, args->scanCode, 2442 metaState, repeatCount, args->downTime); 2443 2444 needWake = enqueueInboundEventLocked(newEntry);下面該InputDispatcherThread登場了。
可以看到,InputDisptacher的主要任務是把前面收到的輸入事件發送到PWM及App端的焦點窗口。前面提到InputReaderThread中收到事件後會調用notifyKey()來通知InputDispatcher,也就是放在mInboundQueue中,在InputDispatcher的dispatchOnce()函數中,會從這個隊列拿出處理。
234 if (!haveCommandsLocked()) { 235 dispatchOnceInnerLocked(&nextWakeupTime); 236 } ... 240 if (runCommandsLockedInterruptible()) { 241 nextWakeupTime = LONG_LONG_MIN; 242 }
其中dispatchOnceInnerLocked()會根據拿出的EventEntry類型調用相應的處理函數,以Key事件為例會調用dispatchKeyLocked():
767 CommandEntry* commandEntry = postCommandLocked( 768 & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); 769 if (mFocusedWindowHandle != NULL) { 770 commandEntry->inputWindowHandle = mFocusedWindowHandle; 771 } 772 commandEntry->keyEntry = entry; ... 791 // Identify targets. 792 Vector它會找到目標窗口,然後通過之前和App間建立的連接發送事件。如果是個需要系統處理的Key事件,這裡會封裝成CommandEntry插入到mCommandQueue隊列中,後面的runCommandLockedInterruptible()函數中會調用doInterceptKeyBeforeDispatchingLockedInterruptible()來讓PWM有機會進行處理。最後dispatchOnce()調用pollOnce()從和App的連接上接收處理完成消息。那麼,InputDispatcher是怎麼確定要往哪個窗口中發事件呢?這裡的成員變量mFocusedWindowHandle指示了焦點窗口,然後findFocusedWindowTargetsLocked()會調用一系列函數(handleTargetsNotReadyLocked(), checkInjectionPermission(), checkWindowReadyForMoreInputLocked()等)檢查mFocusedWindowHandle是否能接收輸入事件。如果可以,將之以InputTarget的形式加到目標窗口數組中。然後就會調用dispatchEventLocked()進行發送。那麼,這個mFocusedWindowHandle是如何維護的呢?為了更好地理解,這裡回頭分析下窗口連接的管理及焦點窗口的管理。inputTargets; 793 int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, 794 entry, inputTargets, nextWakeupTime); .. 804 addMonitoringTargetsLocked(inputTargets); 805 806 // Dispatch the key. 807 dispatchEventLocked(currentTime, entry, inputTargets);
其中與輸入相關的主要有以下幾步:
先創建InputChannel,注意還沒初始化。
521 mInputChannel = new InputChannel();初始化InputChannel,通過WMS的相應接口addToDisplay():
527 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 528 getHostVisibility(), mDisplay.getDisplayId(), 529 mAttachInfo.mContentInsets, mInputChannel);WMS會建立與InputDispatcher的連接。流程如下:
ViewRootImpl通過Session中的addToDisplay()會最終調用WMS的addWindow()。在WMS中,會創建一對InputChannel,本質上是一對本地socket。然後一個注冊給InputDispatcher,一個作為輸出參數傳回給App的ViewRootImpl。這樣就建立了App與IMS的一對連接。
2409 if (outInputChannel != null && (attrs.inputFeatures 2410 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 2411 String name = win.makeInputChannelName(); 2412 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); 2413 win.setInputChannel(inputChannels[0]); 2414 inputChannels[1].transferTo(outInputChannel); 2415 2416 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); 2417 }在InputDispatcher::registerInputChannel()中:
3327 sp這裡創建的Connection表示一個InputDispatcher到應用窗口的連接,裡邊除了用於傳輸的inputChannel,inputPublisher和表示事件接收窗口的inputWindowHandle,還有兩個隊列,outboundQueue是要發的事件,waitQueue是已發事件但還沒有從App端收到完成通知的。這是因為對於一些事件,Input Dispatcher在App沒處理完前一個時不會發第二個。mLooper->addFd()將相應的fd放入InputDispatcher等待的集合中,回調函數為handleReceiveCallback(),也就是說InputDispatcher在收到App發來的消息時是調用它進行處理的。最後調用mLooper->wake()使InputDispatcherThread從epoll_wait()中返回。connection = new Connection(inputChannel, inputWindowHandle, monitor); 3328 3329 int fd = inputChannel->getFd(); 3330 mConnectionsByFd.add(fd, connection); ... 3336 mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
607 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, 608 Looper.myLooper());初始化完後,這個連接的fd就被掛到主線程的等待fd集合去了(InputEventReceiver::nativeInit())。也就是說,當連接上有消息來,主線程就會調用相應的回調處理NativeInputEventReceiver::handleEvent()。
623 // Set up the input pipeline. 624 CharSequence counterSuffix = attrs.getTitle(); 625 mSyntheticInputStage = new SyntheticInputStage(); 626 InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); 627 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, 628 "aq:native-post-ime:" + counterSuffix); 629 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); 630 InputStage imeStage = new ImeInputStage(earlyPostImeStage, 631 "aq:ime:" + counterSuffix); 632 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); 633 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, 634 "aq:native-pre-ime:" + counterSuffix); 635 636 mFirstInputStage = nativePreImeStage; 637 mFirstPostImeInputStage = earlyPostImeStage;到這裡,可以知道,InputDispatcher會維護和WMS中所有窗口的連接,雖然一般只會往焦點窗口發事件。如下所示。
連接建立後,接下來要考慮WMS如何將焦點窗口信息傳給InputDispatcher。舉例來說,當新的窗口加入到WMS中,一般焦點會放到新加窗口上。來看下WMS中的addWindow()函數。
首先,當焦點需要變化時。當焦點窗口變化時,WMS調用
mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);將焦點窗口設到InputMonitor的mInputFocus中。然後調用
mInputMonitor.updateInputWindowsLw(true);來創建InputWindowHandle列表,其中被設成焦點窗口的InputWindowHandle的hasFocus會被置位。之後會調用
mService.mInputManager.setInputWindows(mInputWindowHandles);將這些信息傳到Native層的InputDispatcher。這樣InputDispatcher就能夠知道要往哪個窗口傳事件。在InputDispatcher的setInputWindows()中,會更新InputDispatcher中的焦點窗口句柄。這樣,InputDispatcher中就記錄下了焦點窗口信息。當IMS的InputDispatcher通過InputChannel發事件到焦點窗口時,NativeInputEventReceiver的handleEvent()會被調用。
基本流程比較直觀,先接收事件,然後放入ViewRootImpl的處理隊列,然後dispatch給View處理,經過上面提到的一系列InputStage,最後App處理完事件後還需要向IMS發送一個完成信號。
注意上面是以Key事件為例的。對於Motion事件就有差別了。因為觸摸移動中的事件不一定要每一個都處理,因為顯示也就60HZ,你如果100HZ的輸入事件,全處理只會浪費計算資源。上面這條路是每當InputDispatcher有事件發過來時就會觸發的,而對於Motion事件,系統會把一個VSync周期內的事件存為Batch,當VSync到來時一起處理。從JB開始,App對輸入事件的處理是由VSync信號來驅動的。可以看Choreographer中的VSync回調中首先處理的就是輸入事件。
542 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); 543 doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); 544 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);在非VSync觸發的情況下,NativeInputEventReceiver::handleEvent()調用consumeEvents()時參數consumeBatches為false,通過InputConsumper::consume()函數得知,它會被放到Batch當中:
448 if (canAddSample(batch, &mMsg)) { 449 batch.samples.push(mMsg);循環退出條件不滿足所以一直讀到receiveMessage()失敗,退出後在consumeEvents()中由於返回值不為0所以事件不會被馬上處理;而當VSync信號到來時,下面流程會被觸發,這裡consumeEvents()的參數consumeBatches為true,意味著要處理Batch。
ViewRootImpl中維護了pending input event的列表,用mPendingInputEventHead和mPendingInputEventTail指示,其中的元素為QueuedInputEvent類型。這個隊列會由InputEventReceiver調用enqueueInputEvent()加入元素,然後ViewRootImpl延時或非延時在doProcessInputEvents()中讀出並處理。處理的方法如前圖調用deliverInputEvent(),然後調用InputStage的deliver()方法進行處理。最後調用finishInputEvent()向IMS發送完成信息,它實際是調用了NativeInputEventReceiver::finishInputEvent(),該函數內部使用InputConsumer的sendFinishedSignal()發送處理結束信號。
事實上,當Input resample機制打開時(系統屬性ro_input.noresample控制),對於Motion事件在InputConsumer裡的處理會更復雜一點。Android在InputConsumer中加入了對Motion事件的重采樣。VSync代表Display已完成一幀的顯示,系統需要准備下一幀,也就是說我們需要知道VSync信號來到時觸摸的坐標。那麼問題來了,輸入設備的事件不是按VSync來的,比如是10ms為周期(VSync周期為16.67ms)。那麼怎麼知道VSync的時刻的輸入坐標呢,只能靠估計。怎麼估計呢,靠的是采樣加上內外插值結合的估值方法。詳細可參見http://www.masonchang.com/blog/2014/8/25/androids-touch-resampling-algorithm
。
前面提到,InputDispatcher除了給App端傳事件外,還有個任務是處理系統按鍵。系統中有一些特殊的按鍵,是系統需要處理的,如音量,電源鍵等。它是通過interceptKeyBeforeDispatching()和interceptKeyBeforeQueueing()兩個函數來截獲的。interceptKeyBeforeDispatching()主要用於處理home, menu和search,interceptkeyBeforeQueueing()主要用於處理音量和電源鍵。這些處理的實現都放在PWM中,而調用者是在InputDispatcherThread。這樣的好處是把平台相關的東西都放在PWM中,而InputDispatcher中是平台通用的東西,廠商要定制策略只需修改PWM。這樣,做到了Mechanism和Policy的分離。那InputDisaptcher是如何調用到PWM中的呢?首先InputDispatcher中的代碼中有幾個hook的地方:
3510 nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle, 3511 &event, entry->policyFlags);這裡的mPolicy其實是NativeInputManager。NativeInputManager會通過JNI調用到Java世界中的IMS的相應函數:
1463 // Native callback. 1464 private long interceptKeyBeforeDispatching(InputWindowHandle focus, 1465 KeyEvent event, int policyFlags) { 1466 return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags); 1467 }這裡的mWindowManagerCallbacks其實是InputMonitor。然後就調用到PWM了。
380 public long interceptKeyBeforeDispatching( 381 InputWindowHandle focus, KeyEvent event, int policyFlags) { 382 WindowState windowState = focus != null ? (WindowState) focus.windowState : null; 383 return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags); 384 }好了,到這裡可以小結一下了。輸入事件從Kernel到App和PWM的旅程大體如下圖所示:
可以看到,從Input driver中讀出的是input_event結構,之後在經過中間模塊時會按需對其進行封裝,變成或被封裝進RawEvent, KeyEntry, DispatchEntry, InputMessage, InputEvent, QueuedInputEvent等結構。其中有些結構是對另一些結構的封裝,如DispatchEntry中封裝了KeyEntry,QueuedInputEvent是對InputEvent的封裝等。
回到主線,故事來沒講完。上面說到App這端處理完輸入事件,然後通過和IMS中InputDispacher的通信管道InputChannel發了處理完成通知。那InputDispatcher這邊收到後如何處理呢?
InputDispatcher會調用handleReceiveCallback()來處理完成信號。這裡先是往Command隊列裡放一個處理事務執行doDispatchCycleFinishedLockedInterruptible(),後面在runCommandsLockedInterruptible()中會取出執行。在doDispatchCycleFinishedLockedInterruptible()函數中,會先調用afterKeyEventLockedInterruptible()。Android中可以定義一些Fallback鍵,即如果一個Key事件App沒有處理,可以Fallback成另外默認的Key事件,這是在這裡的dispatchUnhandledKey()函數中進行處理的。接著InputDispatcher會將該收到完成信號的事件項從等待隊列中移除。同時由於上一個事件已被App處理完,就可以調用startDispatchCycleLocked()來進行下一輪事件的處理了。
3558 // Dequeue the event and start the next cycle. 3559 // Note that because the lock might have been released, it is possible that the 3560 // contents of the wait queue to have been drained, so we need to double-check 3561 // a few things. 3562 if (dispatchEntry == connection->findWaitQueueEntry(seq)) { 3563 connection->waitQueue.dequeue(dispatchEntry); ... 3571 } 3572 3573 // Start the next dispatch cycle for this connection. 3574 startDispatchCycleLocked(now(), connection);startDispatchCycleLocked函數會檢查相應連接的輸出緩沖中(connection->outboundQueue)是否有事件要發送的,有的話會通過InputChannel發送出去。
本文將介紹Android設備中的傳感器。傳感器概述(Sensors Overview)大部分Android設備內置了大量的傳感器,比較常見的有測量位移的、感應方向的、感應
上一篇博客,我們介紹了項目分包的結構,這一篇我們重點來介紹一下MVP架構在項目中的應用,MVP可以說是MVC模式的一種升級,在MVP出現之前,一般都是用MVC,但是使用M
很多朋友都說lollipop出來想試用一下,結果在網官下載的android studio 都是20版本,也沒有看見更新到android 5.0。 我也在網上狂了一下,
Drawable基礎什麼是Drawable首先Drawable是一個抽象類,表示的是可以在Canvas中繪制的圖像,常被用作一個view的背景,有多種實現類完成不同的功能