Android開(kāi)發(fā)InputManagerService創(chuàng)建與啟動(dòng)流程
前言
之前寫過(guò)幾篇關(guān)于輸入系統(tǒng)的文章,但是還沒(méi)有寫完,后來(lái)由于工作的變動(dòng),這個(gè)事情就一直耽擱了。而現(xiàn)在,在工作中,遇到輸入系統(tǒng)相關(guān)的事情也越來(lái)越多,其中有一個(gè)非常有意思的需求,因此是時(shí)候繼續(xù)分析 InputManagerService。
InputManagerService 系統(tǒng)文章,基于 Android 12 進(jìn)行分析。
本文將以 IMS 簡(jiǎn)稱 InputManagerService。
啟動(dòng)流程
InputManagerService 是一個(gè)系統(tǒng)服務(wù),啟動(dòng)流程如下
// SystemServer.java private void startOtherServices(@NonNull TimingsTraceAndSlog t) { // .. // 1. 創(chuàng)建 inputManager = new InputManagerService(context); // 注冊(cè)服務(wù) ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); // 保存 wms 的回調(diào) inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); // 2. 啟動(dòng) inputManager.start(); try { // 3. 就緒 if (inputManagerF != null) { inputManagerF.systemRunning(); } } catch (Throwable e) { reportWtf("Notifying InputManagerService running", e); } // ... }
IMS 的啟動(dòng)流程分為三步
- 創(chuàng)建輸入系統(tǒng),建立上層與底層的映射關(guān)系。
- 啟動(dòng)輸入系統(tǒng),其實(shí)就是啟動(dòng)底層輸入系統(tǒng)的幾個(gè)模塊。
- 輸入系統(tǒng)就緒,上層會(huì)同步一些配置給底層輸入系統(tǒng)。
下面分三個(gè)模塊,分別講解這三步。
創(chuàng)建輸入系統(tǒng)
// InputManagerService.java public InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); // 配置為空 mStaticAssociations = loadStaticInputPortAssociations(); // 默認(rèn) false mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); // 1. 底層進(jìn)行初始化 // mPtr 指向底層創(chuàng)建的 NativeInputManager 對(duì)象 mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); // 空 String doubleTouchGestureEnablePath = context.getResources().getString( R.string.config_doubleTouchGestureEnableFile); // null mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath); LocalServices.addService(InputManagerInternal.class, new LocalService()); }
IMS 構(gòu)造函數(shù),主要就是調(diào)用 nativeInit() 來(lái)初始化底層輸入系統(tǒng)。
// com_android_server_input_InputManagerService.cpp static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { // 從Java層的MessageQueue中獲取底層映射的MessageQueue sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == nullptr) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } // 創(chuàng)建 NativeInputManager NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); // 返回指向 NativeInputManager 對(duì)象的指針 return reinterpret_cast<jlong>(im); }
原來(lái)底層創(chuàng)建了 NativeInputManager 對(duì)象,然后返回給上層。
但是 NativeInputManager 并不是底層輸入系統(tǒng)的服務(wù),它只是一個(gè)連接上層輸入系統(tǒng)和底層輸入系統(tǒng)的橋梁而已。來(lái)看下它的創(chuàng)建過(guò)程
// com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); // 1.保存上層的InputManagerService對(duì)象 mServiceObj = env->NewGlobalRef(serviceObj); // 2. 初始化一些參數(shù) { AutoMutex _l(mLock); // mLocked 的類型是 struct Locked,這里初始化了一些參數(shù) // 這些參數(shù)會(huì)被上層改變 mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; mLocked.pointerCapture = false; mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT; } mInteractive = true; // 3.創(chuàng)建并注冊(cè)服務(wù) InputManager mInputManager = new InputManager(this, this); defaultServiceManager()->addService(String16("inputflinger"), mInputManager, false); }
NativeInputManager 構(gòu)造過(guò)程如下
- 創(chuàng)建一個(gè)全局引用,并通過(guò) mServiceObj 指向上層的 InputManagerService 對(duì)象。
- 初始化參數(shù)。這里要注意一個(gè)結(jié)構(gòu)體變量 mLocked,它的一些參數(shù)都是由上層控制的。例如,mLocked.showTouches 是由開(kāi)發(fā)者選項(xiàng)中 "Show taps" 決定的,它的功能是在屏幕上顯示一個(gè)觸摸點(diǎn)。
- 創(chuàng)建并注冊(cè)服務(wù) InputManager。
原來(lái),InputManager 才是底層輸入系統(tǒng)的服務(wù),而 NativeInputManagerService 通過(guò) mServiceObj 保存了上層 InputManagerService 引用,并且上層 InputManagerService 通過(guò) mPtr 指向底層的 NativeInputManager。因此,我們可以判定 NativeInputManager 就是一個(gè)連接上層與底層的橋梁。
我們注意到創(chuàng)建 InputManager 使用了兩個(gè) this 參數(shù),這里介紹下 NativeInputManager 和 InputManager 的結(jié)構(gòu)圖
InputManager 構(gòu)造函數(shù)需要的兩個(gè)接口正好是由 NativeInputManager 實(shí)現(xiàn)的,然而,具體使用這兩個(gè)接口的不是 InputManager,而是它的子模塊。這些子模塊都是在 InputManager 的構(gòu)造函數(shù)中創(chuàng)建的
// InputManager.cpp InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { // 1. 創(chuàng)建InputDispatcher對(duì)象,使用 InputDispatcherPolicyInterface 接口 mDispatcher = createInputDispatcher(dispatcherPolicy); // 2. 創(chuàng)建InputClassifier對(duì)象,使用 InputListenerInterface mClassifier = new InputClassifier(mDispatcher); // 3. 創(chuàng)建InputReader對(duì)象,使用 InputReaderPolicyInterface 和 InputListenerInterface mReader = createInputReader(readerPolicy, mClassifier); } // InputDispatcherFactory.cpp sp<InputDispatcherInterface> createInputDispatcher( const sp<InputDispatcherPolicyInterface>& policy) { return new android::inputdispatcher::InputDispatcher(policy); } // InputReaderFactory.cpp sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) { return new InputReader(std::make_unique<EventHub>(), policy, listener); }
InputManager 構(gòu)造函數(shù)所使用的兩個(gè)接口,分別由 InputDispatcher 和 InputReader 所使用。因此 InputManager 向上通信的能力是由子模塊 InputDispatcher 和 InputReader 實(shí)現(xiàn)的。
InputManager 創(chuàng)建了三個(gè)模塊,InputReader、InputClassifier、InputDispatcher。 InputReader 負(fù)責(zé)從 EventHub 中獲取事件,然后把事件加工后,發(fā)送給 InputClassfier。InputClassifer 會(huì)把事件發(fā)送給 InputDispatcher,但是它會(huì)對(duì)觸摸事件進(jìn)行一個(gè)分類工作。最后 InputDispatcher 對(duì)進(jìn)行事件分發(fā)。
那么現(xiàn)在我們可以大致推算下輸入系統(tǒng)的關(guān)系圖,如下
這個(gè)關(guān)系圖很好的體現(xiàn)了設(shè)計(jì)模式的單一職責(zé)原則。
EventHub 其實(shí)只屬于 InputReader,因此要想解剖整個(gè)輸入系統(tǒng),我們得逐一解剖 InputReader、InputClassifier、InputDispatcher。后面的一系列的文章將逐個(gè)來(lái)剖析。
啟動(dòng)輸入系統(tǒng)
// InputManagerService.java public void start() { Slog.i(TAG, "Starting input manager"); // 1.啟動(dòng)native層 nativeStart(mPtr); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); // 2.監(jiān)聽(tīng)數(shù)據(jù)庫(kù),當(dāng)值發(fā)生改變時(shí),通過(guò) native 層 // 監(jiān)聽(tīng)Settings.System.POINTER_SPEED,這個(gè)表示手指的速度 registerPointerSpeedSettingObserver(); // 監(jiān)聽(tīng)Settings.System.SHOW_TOUCHES,這個(gè)表示是否在屏幕上顯示觸摸坐標(biāo) registerShowTouchesSettingObserver(); // 監(jiān)聽(tīng)Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON registerAccessibilityLargePointerSettingObserver(); // 監(jiān)聽(tīng)Settings.Secure.LONG_PRESS_TIMEOUT,這個(gè)多少毫秒觸發(fā)長(zhǎng)按事件 registerLongPressTimeoutObserver(); // 監(jiān)聽(tīng)用戶切換 mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); updateDeepPressStatusFromSettings("user switched"); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); // 3. 從數(shù)據(jù)庫(kù)獲取值,并傳遞給 native 層 updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); updateDeepPressStatusFromSettings("just booted"); }
輸入系統(tǒng)的啟動(dòng)過(guò)程如下
- 啟動(dòng)底層輸入系統(tǒng)。其實(shí)就是啟動(dòng)剛剛說(shuō)到的 InputReader, InputDispatcher。
- 監(jiān)聽(tīng)一些廣播。因?yàn)檫@些廣播與輸入系統(tǒng)的配置有關(guān),當(dāng)接收到這些廣播,會(huì)更新配置到底層。
- 直接讀取配置,更新到底層輸入系統(tǒng)。
第2步和第3步,本質(zhì)上其實(shí)都是更新配置到底層,但是需要我們對(duì) InputReader 的運(yùn)行過(guò)程比較熟悉,因此這個(gè)配置更新過(guò)程,留到后面分析。
現(xiàn)在我們直接看下如何啟動(dòng)底層的輸入系統(tǒng)
// com_android_server_input_InputManagerService.cpp static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); // 調(diào)用InputManager::start() status_t result = im->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } }
通過(guò) JNI 層的 NativeInputManager 這個(gè)橋梁來(lái)啟動(dòng) InputManager。
前面用一幅圖表明了 NativeInputManager 的橋梁作用,現(xiàn)在感受到了嗎?
status_t InputManager::start() { // 啟動(dòng) Dispatcher status_t result = mDispatcher->start(); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } // 啟動(dòng) InputReader result = mReader->start(); if (result) { ALOGE("Could not start InputReader due to error %d.", result); mDispatcher->stop(); return result; } return OK; }
InputManager 的啟動(dòng)過(guò)程很簡(jiǎn)單,就是直接啟動(dòng)它的子模塊 InputDispatcher 和 InputReader。
InputDispatcher 和 InputReader 的啟動(dòng),都是通過(guò) InputThread 創(chuàng)建一個(gè)線程來(lái)執(zhí)行任務(wù)。
//InputThread.cpp InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake) : mName(name), mThreadWake(wake) { mThread = new InputThreadImpl(loop); mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); }
注意 InputThread 可不是一個(gè)線程,InputThreadImpl 才是一個(gè)線程,如下
//InputThread.cpp class InputThreadImpl : public Thread { public: explicit InputThreadImpl(std::function<void()> loop) : Thread(/* canCallJava */ true), mThreadLoop(loop) {} ~InputThreadImpl() {} private: std::function<void()> mThreadLoop; bool threadLoop() override { mThreadLoop(); return true; } };
當(dāng)線程啟動(dòng)后,會(huì)循環(huán)調(diào)用 threadLoop(),直到這個(gè)函數(shù)返回 false。從 InputThreadImpl 的定義可以看出,threadLoop() 會(huì)一直保持循環(huán),并且每一次循環(huán),會(huì)調(diào)用一次 mThreadLoop(),而函數(shù) mThreadLoop 是由 InputReader 和 InputDispacher 在啟動(dòng)時(shí)傳入
// InputReader.cpp status_t InputReader::start() { if (mThread) { return ALREADY_EXISTS; } // 線程啟動(dòng)后,循環(huán)調(diào)用 loopOnce() mThread = std::make_unique<InputThread>( "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); }); return OK; } // InputDispatcher.cpp status_t InputDispatcher::start() { if (mThread) { return ALREADY_EXISTS; } // 線程啟動(dòng)后,循環(huán)調(diào)用 dispatchOnce() mThread = std::make_unique<InputThread>( "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); }); return OK; }
現(xiàn)在,我們可以明白,InputReader 啟動(dòng)時(shí),會(huì)創(chuàng)建一個(gè)線程,然后循環(huán)調(diào)用 loopOnce() 函數(shù),而 InputDispatcher 啟動(dòng)時(shí),也會(huì)創(chuàng)建一個(gè)線程,然后循環(huán)調(diào)用 dispatchOnce()。
輸入系統(tǒng)就緒
// InputManagerService.java public void systemRunning() { mNotificationManager = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); synchronized (mLidSwitchLock) { mSystemReady = true; // Send the initial lid switch state to any callback registered before the system was // ready. int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID); for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { LidSwitchCallback callback = mLidSwitchCallbacks.get(i); callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP); } } // 監(jiān)聽(tīng)廣播,通知底層加載鍵盤布局 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addDataScheme("package"); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateKeyboardLayouts(); } }, filter, null, mHandler); // 監(jiān)聽(tīng)廣播,通知底層加載設(shè)備別名 filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { reloadDeviceAliases(); } }, filter, null, mHandler); // 直接通知一次底層加載鍵盤布局和加載設(shè)備別名 mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES); mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS); if (mWiredAccessoryCallbacks != null) { mWiredAccessoryCallbacks.systemReady(); } } private void reloadKeyboardLayouts() { nativeReloadKeyboardLayouts(mPtr); } private void reloadDeviceAliases() { nativeReloadDeviceAliases(mPtr); }
無(wú)論是通知底層加載鍵盤布局,還是加載設(shè)備別名,其實(shí)都是讓底層更新配置。與前面一樣,更新配置的過(guò)程,留到后面分析。
結(jié)束
通過(guò)本文,我們能大致掌握輸入系統(tǒng)的輪廓。后面,我們將逐步分析子模塊 InputReader 和 InputDispatcher 的功能。
以上就是Android開(kāi)發(fā)InputManagerService創(chuàng)建與啟動(dòng)流程的詳細(xì)內(nèi)容,更多關(guān)于Android InputManagerService創(chuàng)建啟動(dòng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android?Service啟動(dòng)綁定流程詳解
- Android布局控件View?ViewRootImpl?WindowManagerService關(guān)系
- android?微信搶紅包工具AccessibilityService實(shí)現(xiàn)詳解
- Android?O對(duì)后臺(tái)Service限制詳解
- Android?NotificationListenerService通知監(jiān)聽(tīng)服務(wù)使用
- Android?NotificationListenerService?通知服務(wù)原理解析
- Android 10 啟動(dòng)之servicemanager源碼解析
- Android 開(kāi)機(jī)自啟動(dòng)Service實(shí)現(xiàn)詳解
相關(guān)文章
Android中使用orc實(shí)現(xiàn)文字識(shí)別實(shí)例
這篇文章主要介紹了Android中使用orc實(shí)現(xiàn)文字識(shí)別實(shí)例,詳細(xì)的介紹了orc的簡(jiǎn)介和用法,有興趣的可以了解一下2017-05-05Android仿微信朋友圈全文、收起功能的實(shí)例代碼
本篇文章主要介紹了Android仿微信朋友圈全文、收起功能的實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08Kotlin設(shè)計(jì)模式之委托模式使用方法詳解
Kotlin提供了兩個(gè)本機(jī)功能來(lái)實(shí)現(xiàn)委托模式,第一個(gè)是接口委托(例如策略模式),另一種是屬性委托,它專注于類成員/屬性(例如延遲加載、observable等),它們共同提供了一組豐富而簡(jiǎn)潔的功能,通過(guò)本博客,您將了解在什么情況下使用此模式2023-09-09AndroidStudio kotlin配置詳細(xì)介紹
這篇文章主要介紹了AndroidStudio kotlin配置詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-05-05Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入)
本文主要介紹了Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Android編程之Button控件配合Toast控件用法分析
這篇文章主要介紹了Android編程之Button控件配合Toast控件用法,結(jié)合實(shí)例形式分析了Button控件及Toast控件的功能及具體使用技巧,需要的朋友可以參考下2015-12-12Android基于ImageView繪制的開(kāi)關(guān)按鈕效果示例
這篇文章主要介紹了Android基于ImageView繪制的開(kāi)關(guān)按鈕效果,結(jié)合實(shí)例形式分析了Android使用ImageView進(jìn)行按鈕繪制的界面布局、功能實(shí)現(xiàn)及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-03-03Android Material加載進(jìn)度條制作代碼
這篇文章主要為大家詳細(xì)介紹了AndroidMaterial加載進(jìn)度條的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android 項(xiàng)目實(shí)戰(zhàn)之頭像選擇功能
這篇文章主要介紹了Android 項(xiàng)目實(shí)戰(zhàn)頭像選擇功能實(shí)戰(zhàn),需要的朋友可以參考下2018-01-01