溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android中怎么利用 Input子系統(tǒng)監(jiān)聽線程的啟動

發(fā)布時間:2021-06-28 16:03:57 來源:億速云 閱讀:164 作者:Leah 欄目:移動開發(fā)

今天就跟大家聊聊有關(guān)Android中怎么利用 Input子系統(tǒng)監(jiān)聽線程的啟動,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

InputManagerService初始化概覽

首先,有幾點共識我們都可以達(dá)成:

  • Android  Framework層的Service(Java)都是由system_server進(jìn)程創(chuàng)建的(由于沒有fork,因此都運(yùn)行在system_server進(jìn)程中)

  • Service創(chuàng)建后就會交給運(yùn)行在system_server進(jìn)程中的ServiceManager管理。

因此對于InputManagerService的創(chuàng)建,我們可以在SystemServer的startOtherServices()方法中找到,該方法做了以下事情:

  • 創(chuàng)建InputManagerService對象

  • 將它交給ServiceManager管理

  • 將WindowManagerService的InputMonitor注冊到InputManagerService中作為窗口響應(yīng)事件后的回調(diào)

  • 完成以上工作后啟動InputManagerService。 

SystemServer.javastartOtherServices(){     ……     inputManager = new InputManagerService(context);     ……     inputManager.setWindowManagerCallbacks(wm.getInputMonitor());     inputManager.start();     …… }

接下來我們就逐部分學(xué)習(xí)相應(yīng)的處理。

InputManagerService對象的創(chuàng)建

創(chuàng)建InputManagerService對象時會完成以下工作:

  • 創(chuàng)建一個負(fù)責(zé)處理DisplayThread線程中的Message的Handler

  • 調(diào)用nativeInit初始化native層的InputManagerService,初始化的時候傳入了DisplayThread的消息隊列

  • 用mPtr保存native層的InputManagerService

  • 初始化完成后將Service添加到LocalServices,通過Map以鍵值對的形式存儲 

InputManagerService.javapublic InputManagerService(Context context) {    this.mContext = context;    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());      mUseDevInputEventForAudioJack =             context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);     Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="             + mUseDevInputEventForAudioJack);     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());      LocalServices.addService(InputManagerInternal.class, new LocalService()); }

這里可能有人就會問了,為什么InputManagerService要和DisplayThread綁定在一起?大家不妨想想,InputEvent無論如何被獲取、歸類、分發(fā),最終還是要被處理,也就意味著最終它的處理結(jié)果都要在UI上體現(xiàn),那么InputManagerService自然要選擇和UI親近一些的線程在一起了。

但是問題又來了,應(yīng)用都是運(yùn)行在自己的主線程里的,難道InputManagerService要一個個綁定么,還是一個個輪詢?這些做法都太過低效,那換個辦法,可不可以和某個管理或非常親近所有應(yīng)用UI的線程綁定在一起呢?

答案是什么,我在這里先不說,大家可以利用自己的知識想想。

初始化native層的InputManagerService

在nativeInit函數(shù)中,將Java層的MessageQueue轉(zhuǎn)換為native層的MessageQueue,然后再取出Looper用于NativeInputManager的初始化。可見這里的重頭戲就是NativeInputManager的創(chuàng)建,這個過程做了以下事情:

  • 將Java層的Context和InputManagerService轉(zhuǎn)換為native層的Context和InputManagerService存儲在mContextObj和mServiceObj中

  • 初始化變量

  • 創(chuàng)建EventHub

  • 創(chuàng)建InputManager 

com_android_server_input_InputManagerService.cpp  NativeInputManager::NativeInputManager(jobject contextObj,         jobject serviceObj, const sp<Looper>& looper) :         mLooper(looper), mInteractive(true) {     JNIEnv* env = jniEnv();      mContextObj = env->NewGlobalRef(contextObj);     mServiceObj = env->NewGlobalRef(serviceObj);      {        AutoMutex _l(mLock);         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;         mLocked.pointerSpeed = 0;         mLocked.pointerGesturesEnabled = true;         mLocked.showTouches = false;     }     mInteractive = true;      sp<EventHub> eventHub = new EventHub();     mInputManager = new InputManager(eventHub, this, this); }

EventHub

看到這里很多人就會想,EventHub是什么?取英語釋義來看,它的意思是事件樞紐。我們在文章開頭的時候也提到過,Input系統(tǒng)的事件來源于驅(qū)動/內(nèi)核,那么我們可以猜測EventHub是處理來自驅(qū)動/內(nèi)核的元事件的樞紐。接下來就在源碼中驗證我們的想法吧。

EventHub的創(chuàng)建過程中做了以下事情:

  • 創(chuàng)建mEpollFd用于監(jiān)聽是否有數(shù)據(jù)(有無事件)可讀

  • 創(chuàng)建mINotifyFd將它注冊到DEVICE_PATH(這里路徑就是/dev/input)節(jié)點,并將它交給內(nèi)核用于監(jiān)聽該設(shè)備節(jié)點的增刪數(shù)據(jù)事件。那么只要有數(shù)據(jù)增刪的事件到來,epoll_wait()就會返回,使得EventHub能收到來自系統(tǒng)的通知,并獲取事件的詳細(xì)信息

  • 調(diào)用epoll_ctl函數(shù)將mEpollFd和mINotifyFd注冊到epoll中

  • 定義int wakeFd[2]作為事件傳輸管道的讀寫兩端,并將讀端注冊到epoll中讓mEpollFd監(jiān)聽 

EventHub.cpp  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);      mEpollFd = epoll_create(EPOLL_SIZE_HINT);     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);      mINotifyFd = inotify_init();     int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);     &hellip;&hellip;     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);     &hellip;&hellip;     int wakeFds[2];     result = pipe(wakeFds);     &hellip;&hellip;     mWakeReadPipeFd = wakeFds[0];     mWakeWritePipeFd = wakeFds[1];      result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);     &hellip;&hellip;     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);     &hellip;&hellip;     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);     &hellip;&hellip; }

  Android中怎么利用 Input子系統(tǒng)監(jiān)聽線程的啟動

那么這里拋出一個問題:為什么要把管道的讀端注冊到epoll中?假如EventHub因為getEvents讀不到事件而阻塞在epoll_wait()里,而我們沒有綁定讀端的話,我們要怎么喚醒EventHub?如果綁定了管道的讀端,我們就可以通過向管道的寫端寫數(shù)據(jù)從而讓EventHub因為得到管道寫端的數(shù)據(jù)而被喚醒。

InputManager的創(chuàng)建

接下來繼續(xù)說InputManager的創(chuàng)建,它的創(chuàng)建就簡單多了,創(chuàng)建一個InputDispatcher對象用于分發(fā)事件,一個InputReader對象用于讀事件并把事件交給InputDispatcher分發(fā),,然后調(diào)用initialize()初始化,其實也就是創(chuàng)建了InputReaderThread和InputDispatcherThread。

InputManager.cpp  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(); }void InputManager::initialize() {     mReaderThread = new InputReaderThread(mReader);     mDispatcherThread = new InputDispatcherThread(mDispatcher); }

InputDispatcher和InputReader的創(chuàng)建都相對簡單。InputDispatcher會創(chuàng)建自己線程的Looper,以及設(shè)置根據(jù)傳入的dispatchPolicy設(shè)置分發(fā)規(guī)則。InputReader則會將傳入的InputDispatcher封裝為監(jiān)聽對象存起來,做一些數(shù)據(jù)初始化就結(jié)束了。

至此,InputManagerService對象的初始化就完成了,根據(jù)開頭說的,接下來就會調(diào)用InputManagerService的start()方法。

監(jiān)聽線程InputReader和InputDispatcher的啟動

在start()方法中,做了以下事情:

  • 調(diào)用nativeStart方法,其實就是調(diào)用InputManager的start()方法

  • 將InputManagerService交給WatchDog監(jiān)控

  • 注冊觸控點速度、顯示觸控的觀察者,并注冊廣播監(jiān)控它們

  • 主動調(diào)用updateXXX方法更新(初始化) 

InputManagerService.javapublic void start() {     Slog.i(TAG, "Starting input manager");     nativeStart(mPtr);    // Add ourself to the Watchdog monitors.     Watchdog.getInstance().addMonitor(this);      registerPointerSpeedSettingObserver();     registerShowTouchesSettingObserver();     registerAccessibilityLargePointerSettingObserver();      mContext.registerReceiver(new BroadcastReceiver() {        @Override         public void onReceive(Context context, Intent intent) {             updatePointerSpeedFromSettings();             updateShowTouchesFromSettings();             updateAccessibilityLargePointerFromSettings();         }     }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);      updatePointerSpeedFromSettings();     updateShowTouchesFromSettings();     updateAccessibilityLargePointerFromSettings(); }

顯而易見這里最值得關(guān)注的就是InputManager的start()方法了,可惜這個方法并不值得我們?nèi)绱岁P(guān)心,因為它做的事情很簡單,就是啟動InputDispatcherThread和InputReaderThread開始監(jiān)聽。

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; }

那么InputReaderThread線程是怎么和EventHub關(guān)聯(lián)起來的呢?

對于InputReadThread:

  • 啟動后循環(huán)執(zhí)行mReader->loopOnce()

  • loopOnce()中會調(diào)用mEventHub->getEvents讀取事件

  • 讀到了事件就會調(diào)用processEventsLocked處理事件

  • 處理完成后調(diào)用getInputDevicesLocked獲取輸入設(shè)備信息

  • 調(diào)用mPolicy->notifyInputDevicesChanged函數(shù)利用InputManagerService的代理通過Handler發(fā)送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知輸入設(shè)備發(fā)生了變化

  • ***調(diào)用mQueuedListener->flush(),將事件隊列中的所有事件交給在InputReader中注冊過的InputDispatcher 

bool InputReaderThread::threadLoop() {     mReader->loopOnce();    return true; }void InputReader::loopOnce() {     &hellip;&hellip;      size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);      { // acquire lock         AutoMutex _l(mLock);         mReaderIsAliveCondition.broadcast();        if (count) {             processEventsLocked(mEventBuffer, count);         }      &hellip;&hellip;        if (oldGeneration != mGeneration) {             inputDevicesChanged = true;             getInputDevicesLocked(inputDevices);         }     } // release lock      // Send out a message that the describes the changed input devices.     if (inputDevicesChanged) {         mPolicy->notifyInputDevicesChanged(inputDevices);     }      &hellip;&hellip;     mQueuedListener->flush(); }

看完上述內(nèi)容,你們對Android中怎么利用 Input子系統(tǒng)監(jiān)聽線程的啟動有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI