這篇文章主要講解了“Android中如何利用Input子系統(tǒng)監(jiān)聽線程的啟動(dòng)”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Android中如何利用Input子系統(tǒng)監(jiān)聽線程的啟動(dòng)”吧!
目前累計(jì)服務(wù)客戶成百上千,積累了豐富的產(chǎn)品開發(fā)及服務(wù)經(jīng)驗(yàn)。以網(wǎng)站設(shè)計(jì)水平和技術(shù)實(shí)力,樹立企業(yè)形象,為客戶提供做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、網(wǎng)站策劃、網(wǎng)頁設(shè)計(jì)、網(wǎng)絡(luò)營銷、VI設(shè)計(jì)、網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。創(chuàng)新互聯(lián)始終以務(wù)實(shí)、誠信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過對領(lǐng)先技術(shù)的掌握、對創(chuàng)意設(shè)計(jì)的研究、對客戶形象的視覺傳遞、對應(yīng)用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進(jìn)步。InputManagerService初始化概覽
首先,有幾點(diǎn)共識我們都可以達(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)
完成以上工作后啟動(dòng)InputManagerService。
SystemServer.javastartOtherServices(){ …… inputManager = new InputManagerService(context); …… inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); …… }
接下來我們就逐部分學(xué)習(xí)相應(yīng)的處理。
InputManagerService對象的創(chuàng)建
創(chuàng)建InputManagerService對象時(shí)會完成以下工作:
創(chuàng)建一個(gè)負(fù)責(zé)處理DisplayThread線程中的Message的Handler
調(diào)用nativeInit初始化native層的InputManagerService,初始化的時(shí)候傳入了DisplayThread的消息隊(duì)列
用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要一個(gè)個(gè)綁定么,還是一個(gè)個(gè)輪詢?這些做法都太過低效,那換個(gè)辦法,可不可以和某個(gè)管理或非常親近所有應(yīng)用UI的線程綁定在一起呢?
答案是什么,我在這里先不說,大家可以利用自己的知識想想。
初始化native層的InputManagerService
在nativeInit函數(shù)中,將Java層的MessageQueue轉(zhuǎn)換為native層的MessageQueue,然后再取出Looper用于NativeInputManager的初始化。可見這里的重頭戲就是NativeInputManager的創(chuàng)建,這個(gè)過程做了以下事情:
將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是什么?取英語釋義來看,它的意思是事件樞紐。我們在文章開頭的時(shí)候也提到過,Input系統(tǒng)的事件來源于驅(qū)動(dòng)/內(nèi)核,那么我們可以猜測EventHub是處理來自驅(qū)動(dòng)/內(nèi)核的元事件的樞紐。接下來就在源碼中驗(yàn)證我們的想法吧。
EventHub的創(chuàng)建過程中做了以下事情:
創(chuàng)建mEpollFd用于監(jiān)聽是否有數(shù)據(jù)(有無事件)可讀
創(chuàng)建mINotifyFd將它注冊到DEVICE_PATH(這里路徑就是/dev/input)節(jié)點(diǎn),并將它交給內(nèi)核用于監(jiān)聽該設(shè)備節(jié)點(diǎn)的增刪數(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); …… 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); …… result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); …… }
那么這里拋出一個(gè)問題:為什么要把管道的讀端注冊到epoll中?假如EventHub因?yàn)間etEvents讀不到事件而阻塞在epoll_wait()里,而我們沒有綁定讀端的話,我們要怎么喚醒EventHub?如果綁定了管道的讀端,我們就可以通過向管道的寫端寫數(shù)據(jù)從而讓EventHub因?yàn)榈玫焦艿缹懚说臄?shù)據(jù)而被喚醒。
InputManager的創(chuàng)建
接下來繼續(xù)說InputManager的創(chuàng)建,它的創(chuàng)建就簡單多了,創(chuàng)建一個(gè)InputDispatcher對象用于分發(fā)事件,一個(gè)InputReader對象用于讀事件并把事件交給InputDispatcher分發(fā),,然后調(diào)用initialize()初始化,其實(shí)也就是創(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的啟動(dòng)
在start()方法中,做了以下事情:
調(diào)用nativeStart方法,其實(shí)就是調(diào)用InputManager的start()方法
將InputManagerService交給WatchDog監(jiān)控
注冊觸控點(diǎn)速度、顯示觸控的觀察者,并注冊廣播監(jiān)控它們
主動(dòng)調(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()方法了,可惜這個(gè)方法并不值得我們?nèi)绱岁P(guān)心,因?yàn)樗龅氖虑楹芎唵?,就是啟?dòng)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:
啟動(dòng)后循環(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(),將事件隊(duì)列中的所有事件交給在InputReader中注冊過的InputDispatcher
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }void InputReader::loopOnce() { …… size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } …… 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); } …… mQueuedListener->flush(); }
感謝各位的閱讀,以上就是“Android中如何利用Input子系統(tǒng)監(jiān)聽線程的啟動(dòng)”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Android中如何利用Input子系統(tǒng)監(jiān)聽線程的啟動(dòng)這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
標(biāo)題名稱:Android中如何利用Input子系統(tǒng)監(jiān)聽線程的啟動(dòng)-創(chuàng)新互聯(lián)
網(wǎng)頁鏈接:http://bm7419.com/article32/iiisc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、商城網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、服務(wù)器托管、云服務(wù)器、品牌網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)