自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android Input子系統(tǒng):Input進(jìn)程的創(chuàng)建,監(jiān)聽線程的啟動(dòng)

移動(dòng)開發(fā) Android
從我個(gè)人的理解來看,Android的Input系統(tǒng)其實(shí)就是系統(tǒng)級(jí)的事件處理、分發(fā)框架,它需要的功能模塊大致有:事件讀取、事件分類、事件分發(fā)。那么我們就從整個(gè)Input系統(tǒng)的輸入源入手,了解事件是如何被輸入到Input系統(tǒng)中的。

本文主要從系統(tǒng)源碼的角度帶你一步步了解Android Input子系統(tǒng)。

從我個(gè)人的理解來看,Android的Input系統(tǒng)其實(shí)就是系統(tǒng)級(jí)的事件處理、分發(fā)框架,它需要的功能模塊大致有:事件讀取、事件分類、事件分發(fā)。那么我們就從整個(gè)Input系統(tǒng)的輸入源入手,了解事件是如何被輸入到Input系統(tǒng)中的。

在看代碼前我們先想一想,如果要我們?cè)O(shè)計(jì)一個(gè)事件分發(fā)框架的輸入讀取模塊,要考慮到哪些子模塊:

  • 事件生成模塊(當(dāng)用戶對(duì)設(shè)備進(jìn)行操作產(chǎn)生InputEvent,硬件產(chǎn)生中斷將事件交給驅(qū)動(dòng),驅(qū)動(dòng)交給內(nèi)核,內(nèi)核交給framework)
  • 事件監(jiān)聽模塊(這里就很像設(shè)計(jì)一個(gè)服務(wù)器,為了及時(shí)響應(yīng)來自客戶端的請(qǐng)求,則需要啟動(dòng)一個(gè)線程監(jiān)聽)
  • 事件讀取模塊
  • 事件分發(fā)模塊

那么現(xiàn)在我們最起碼可以知道整個(gè)學(xué)習(xí)的起點(diǎn)了,就是Input系統(tǒng)中,負(fù)責(zé)監(jiān)聽的線程是誰,監(jiān)聽的過程中它們做了什么。 在開始之前,給大家分享一張我根據(jù)本文內(nèi)容畫的圖:

Android Input子系統(tǒng):Input進(jìn)程的創(chuàng)建,監(jiān)聽線程的啟動(dòng)

InputManagerService初始化概覽

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

  • Android Framework層的Service(Java)都是由system_server進(jìn)程創(chuàng)建的(由于沒有fork,因此都運(yùn)行在system_server進(jìn)程中)
  • Service創(chuàng)建后就會(huì)交給運(yùn)行在system_server進(jìn)程中的ServiceManager管理。

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

  • 創(chuàng)建InputManagerService對(duì)象
  • 將它交給ServiceManager管理
  • 將WindowManagerService的InputMonitor注冊(cè)到InputManagerService中作為窗口響應(yīng)事件后的回調(diào)
  • 完成以上工作后啟動(dòng)InputManagerService。 
  1. SystemServer.javastartOtherServices(){ 
  2.     …… 
  3.     inputManager = new InputManagerService(context); 
  4.     …… 
  5.     inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); 
  6.     inputManager.start(); 
  7.     …… 

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

InputManagerService對(duì)象的創(chuàng)建

創(chuàng)建InputManagerService對(duì)象時(shí)會(huì)完成以下工作:

  • 創(chuàng)建一個(gè)負(fù)責(zé)處理DisplayThread線程中的Message的Handler
  • 調(diào)用nativeInit初始化native層的InputManagerService,初始化的時(shí)候傳入了DisplayThread的消息隊(duì)列
  • 用mPtr保存native層的InputManagerService
  • 初始化完成后將Service添加到LocalServices,通過Map以鍵值對(duì)的形式存儲(chǔ) 
  1. InputManagerService.javapublic InputManagerService(Context context) {    this.mContext = context;    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); 
  2.  
  3.     mUseDevInputEventForAudioJack = 
  4.             context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); 
  5.     Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" 
  6.             + mUseDevInputEventForAudioJack); 
  7.     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); 
  8.  
  9.     LocalServices.addService(InputManagerInternal.class, new LocalService()); 

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

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

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

初始化native層的InputManagerService

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

  • 將Java層的Context和InputManagerService轉(zhuǎn)換為native層的Context和InputManagerService存儲(chǔ)在mContextObj和mServiceObj中
  • 初始化變量
  • 創(chuàng)建EventHub
  • 創(chuàng)建InputManager 
  1. com_android_server_input_InputManagerService.cpp 
  2.  
  3. NativeInputManager::NativeInputManager(jobject contextObj, 
  4.         jobject serviceObj, const sp<Looper>& looper) : 
  5.         mLooper(looper), mInteractive(true) { 
  6.     JNIEnv* env = jniEnv(); 
  7.  
  8.     mContextObj = env->NewGlobalRef(contextObj); 
  9.     mServiceObj = env->NewGlobalRef(serviceObj); 
  10.  
  11.     {        AutoMutex _l(mLock); 
  12.         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; 
  13.         mLocked.pointerSpeed = 0; 
  14.         mLocked.pointerGesturesEnabled = true
  15.         mLocked.showTouches = false
  16.     } 
  17.     mInteractive = true
  18.  
  19.     sp<EventHub> eventHub = new EventHub(); 
  20.     mInputManager = new InputManager(eventHub, this, this); 

 EventHub

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

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

  • 創(chuàng)建mEpollFd用于監(jiān)聽是否有數(shù)據(jù)(有無事件)可讀
  • 創(chuàng)建mINotifyFd將它注冊(cè)到DEVICE_PATH(這里路徑就是/dev/input)節(jié)點(diǎn),并將它交給內(nèi)核用于監(jiān)聽該設(shè)備節(jié)點(diǎn)的增刪數(shù)據(jù)事件。那么只要有數(shù)據(jù)增刪的事件到來,epoll_wait()就會(huì)返回,使得EventHub能收到來自系統(tǒng)的通知,并獲取事件的詳細(xì)信息
  • 調(diào)用epoll_ctl函數(shù)將mEpollFd和mINotifyFd注冊(cè)到epoll中
  • 定義int wakeFd[2]作為事件傳輸管道的讀寫兩端,并將讀端注冊(cè)到epoll中讓mEpollFd監(jiān)聽 
  1. EventHub.cpp 
  2.  
  3. EventHub::EventHub(void) : 
  4.         mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), 
  5.         mOpeningDevices(0), mClosingDevices(0), 
  6.         mNeedToSendFinishedDeviceScan(false), 
  7.         mNeedToReopenDevices(false), mNeedToScanDevices(true), 
  8.         mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { 
  9.     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); 
  10.  
  11.     mEpollFd = epoll_create(EPOLL_SIZE_HINT); 
  12.     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno); 
  13.  
  14.     mINotifyFd = inotify_init(); 
  15.     int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); 
  16.     …… 
  17.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); 
  18.     …… 
  19.     int wakeFds[2]; 
  20.     result = pipe(wakeFds); 
  21.     …… 
  22.     mWakeReadPipeFd = wakeFds[0]; 
  23.     mWakeWritePipeFd = wakeFds[1]; 
  24.  
  25.     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); 
  26.     …… 
  27.     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); 
  28.     …… 
  29.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); 
  30.     …… 

  Android Input子系統(tǒng):Input進(jìn)程的創(chuàng)建,監(jiān)聽線程的啟動(dòng)

那么這里拋出一個(gè)問題:為什么要把管道的讀端注冊(cè)到epoll中?假如EventHub因?yàn)間etEvents讀不到事件而阻塞在epoll_wait()里,而我們沒有綁定讀端的話,我們要怎么喚醒EventHub?如果綁定了管道的讀端,我們就可以通過向管道的寫端寫數(shù)據(jù)從而讓EventHub因?yàn)榈玫焦艿缹懚说臄?shù)據(jù)而被喚醒。

InputManager的創(chuàng)建

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

  1. InputManager.cpp 
  2.  
  3. InputManager::InputManager(        const sp<EventHubInterface>& eventHub,        const sp<InputReaderPolicyInterface>& readerPolicy,        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { 
  4.     mDispatcher = new InputDispatcher(dispatcherPolicy); 
  5.     mReader = new InputReader(eventHub, readerPolicy, mDispatcher); 
  6.     initialize(); 
  7. }void InputManager::initialize() { 
  8.     mReaderThread = new InputReaderThread(mReader); 
  9.     mDispatcherThread = new InputDispatcherThread(mDispatcher); 

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

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

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

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

  • 調(diào)用nativeStart方法,其實(shí)就是調(diào)用InputManager的start()方法
  • 將InputManagerService交給WatchDog監(jiān)控
  • 注冊(cè)觸控點(diǎn)速度、顯示觸控的觀察者,并注冊(cè)廣播監(jiān)控它們
  • 主動(dòng)調(diào)用updateXXX方法更新(初始化) 
  1. InputManagerService.javapublic void start() { 
  2.     Slog.i(TAG, "Starting input manager"); 
  3.     nativeStart(mPtr);    // Add ourself to the Watchdog monitors. 
  4.     Watchdog.getInstance().addMonitor(this); 
  5.  
  6.     registerPointerSpeedSettingObserver(); 
  7.     registerShowTouchesSettingObserver(); 
  8.     registerAccessibilityLargePointerSettingObserver(); 
  9.  
  10.     mContext.registerReceiver(new BroadcastReceiver() {        @Override 
  11.         public void onReceive(Context context, Intent intent) { 
  12.             updatePointerSpeedFromSettings(); 
  13.             updateShowTouchesFromSettings(); 
  14.             updateAccessibilityLargePointerFromSettings(); 
  15.         } 
  16.     }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); 
  17.  
  18.     updatePointerSpeedFromSettings(); 
  19.     updateShowTouchesFromSettings(); 
  20.     updateAccessibilityLargePointerFromSettings(); 

 顯而易見這里最值得關(guān)注的就是InputManager的start()方法了,可惜這個(gè)方法并不值得我們?nèi)绱岁P(guān)心,因?yàn)樗龅氖虑楹芎?jiǎn)單,就是啟動(dòng)InputDispatcherThread和InputReaderThread開始監(jiān)聽。 

  1. status_t InputManager::start() { 
  2.     status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);    if (result) { 
  3.         ALOGE("Could not start InputDispatcher thread due to error %d.", result);        return result; 
  4.     } 
  5.  
  6.     result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);    if (result) { 
  7.         ALOGE("Could not start InputReader thread due to error %d.", result); 
  8.  
  9.         mDispatcherThread->requestExit();        return result; 
  10.     }    return OK; 

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

對(duì)于InputReadThread:

  • 啟動(dòng)后循環(huán)執(zhí)行mReader->loopOnce()
  • loopOnce()中會(huì)調(diào)用mEventHub->getEvents讀取事件
  • 讀到了事件就會(huì)調(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(),將事件隊(duì)列中的所有事件交給在InputReader中注冊(cè)過的InputDispatcher 
  1. bool InputReaderThread::threadLoop() { 
  2.     mReader->loopOnce();    return true
  3. }void InputReader::loopOnce() { 
  4.     …… 
  5.  
  6.     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); 
  7.  
  8.     { // acquire lock 
  9.         AutoMutex _l(mLock); 
  10.         mReaderIsAliveCondition.broadcast();        if (count) { 
  11.             processEventsLocked(mEventBuffer, count); 
  12.         } 
  13.  
  14.     ……        if (oldGeneration != mGeneration) { 
  15.             inputDevicesChanged = true
  16.             getInputDevicesLocked(inputDevices); 
  17.         } 
  18.     } // release lock 
  19.  
  20.     // Send out a message that the describes the changed input devices. 
  21.     if (inputDevicesChanged) { 
  22.         mPolicy->notifyInputDevicesChanged(inputDevices); 
  23.     } 
  24.  
  25.     …… 
  26.     mQueuedListener->flush(); 

至此,Input系統(tǒng)有關(guān)事件輸入模塊的學(xué)習(xí)就結(jié)束了,在后續(xù)的文章中會(huì)繼續(xù)學(xué)習(xí)Input系統(tǒng)的事件歸類、分發(fā)流程,感興趣的朋友可以留意關(guān)注。

 

責(zé)任編輯:未麗燕 來源: 知乎專欄
相關(guān)推薦

2021-08-31 11:53:38

Linux inputLinux 系統(tǒng)

2017-02-28 18:26:09

Linuxinput子系統(tǒng)編程

2016-10-28 21:30:00

AndroidJava進(jìn)程

2011-02-14 10:49:40

HTML 5

2009-07-08 10:56:04

WebWork

2020-08-23 08:59:35

number修飾鍵代碼

2014-12-11 16:40:31

Android|進(jìn)程線程

2022-04-01 15:18:04

HarmonyHDF 驅(qū)動(dòng)鴻蒙

2013-01-06 13:06:02

2012-05-04 09:49:34

進(jìn)程

2011-09-19 18:56:17

啟動(dòng)磁盤Vista

2018-07-06 14:00:55

Linux進(jìn)程線程

2021-07-05 09:35:36

鴻蒙HarmonyOS應(yīng)用

2021-07-22 08:03:08

Windows 操作系統(tǒng)Linux

2021-12-13 22:52:37

iphone iOSHTML

2021-06-17 07:55:34

線程進(jìn)程COW

2019-02-26 11:15:25

進(jìn)程多線程多進(jìn)程

2023-02-07 08:01:51

失焦邏輯input

2021-08-10 11:30:30

Linux代碼中斷控制器

2021-08-03 15:10:26

Linux代碼驅(qū)動(dòng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)