中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

西昌網(wǎng)站建設(shè)我想在百度上發(fā)布廣告怎么發(fā)

西昌網(wǎng)站建設(shè),我想在百度上發(fā)布廣告怎么發(fā),做空間的網(wǎng)站嗎,蘇州區(qū)建設(shè)局網(wǎng)站文章目錄 前言一、輸入系統(tǒng)的基本組成部分二、輸入系統(tǒng)相關(guān)源碼分析1、IMS 構(gòu)建1.1、SystemServer # startOtherServices()1.2、InputManagerService1.3、NativeInputManager # nativeInit()1.4、NativeInputManager1.5、InputManager1.6、InputDispatcher1.7、InputReader1.8、…

文章目錄

  • 前言
  • 一、輸入系統(tǒng)的基本組成部分
  • 二、輸入系統(tǒng)相關(guān)源碼分析
    • 1、IMS 構(gòu)建
      • 1.1、SystemServer # startOtherServices()
      • 1.2、InputManagerService
      • 1.3、NativeInputManager # nativeInit()
      • 1.4、NativeInputManager
      • 1.5、InputManager
      • 1.6、InputDispatcher
      • 1.7、InputReader
      • 1.8、EventHub
      • 1.9、小結(jié)
    • 2、IMS 啟動(dòng)
      • 2.1、IMS # start()
      • 2.2、NativeInputManager # nativeStart()
      • 2.3、InputManager # start()
      • 2.4、InputDispatcher # start()
      • 2.5、InputReader # start()
      • 2.6、InputThread
    • 3、IMS 系統(tǒng)就緒
  • 三、總結(jié)
    • 1、IMS 啟動(dòng)時(shí)序圖
    • 2、IMS 成員關(guān)系圖
  • 參考

前言

Android 輸入系統(tǒng)(Input System) 的工作原理,包括:輸入設(shè)備的管理、輸入事件的加工方式及派發(fā)流程。首先輸入設(shè)備包括:觸摸屏,鍵盤,鼠標(biāo)和手柄等,其中觸摸屏與鍵盤是 Android 最普遍也是最標(biāo)準(zhǔn)的輸入設(shè)備。當(dāng)用戶操作輸入設(shè)備時(shí),Linux 內(nèi)核接收到相應(yīng)的硬件中斷,然后將中斷加工成原始的輸入事件數(shù)據(jù)并寫入其對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)中,在用戶空間可以通過(guò)輸入系統(tǒng)內(nèi)部的讀取函數(shù)將原始事件數(shù)據(jù)讀出,并進(jìn)行一系列的翻譯加工成 Android 輸入事件,然后在所有的窗口中尋找合適的事件接收者,并派發(fā)給它來(lái)消費(fèi)該輸入事件??梢?#xff0c;輸入系統(tǒng)在整個(gè)輸入事件處理過(guò)程中起到了承上啟下的銜接作用。


一、輸入系統(tǒng)的基本組成部分

IMS 的總體流程與參與者
上圖展示了輸入事件的處理流程以及輸入系統(tǒng)中最基本的參與者,下面簡(jiǎn)要介紹一下各個(gè)參與者:

  • Linux 內(nèi)核:接受輸入設(shè)備的中斷,并將原始輸入事件的數(shù)據(jù)寫入設(shè)備節(jié)點(diǎn)中;
  • 設(shè)備節(jié)點(diǎn):內(nèi)核與 InputManagerService 的橋梁,它將原始事件的數(shù)據(jù)暴露給用戶空間,以便 InputManagerService 可以從中讀取事件;
  • InputManagerService:Android 系統(tǒng)服務(wù),以后簡(jiǎn)稱 IMS,其分為 Java 層和 Native 層兩部分。Java 層負(fù)責(zé)與 WindowManagerService 通信。而 Native 層則是InputReaderInputDispatcher 兩個(gè)輸入系統(tǒng)關(guān)鍵組件的運(yùn)行容器;
  • EventHub:直接訪問(wèn)所有的設(shè)備節(jié)點(diǎn)。并且正如其名字所描述的,它通過(guò)一個(gè)名為 getEvents( ) 的函數(shù)將所有輸入系統(tǒng)相關(guān)的待處理的底層事件返回給使用者,包括原始輸入事件、設(shè)備節(jié)點(diǎn)的增刪等。
  • InputReaderIMS 中的關(guān)鍵組件之一,運(yùn)行于一個(gè)獨(dú)立的線程中,負(fù)責(zé)管理輸入設(shè)備的列表與配置,以及進(jìn)行輸入事件的加工處理。通過(guò)其線程循環(huán)不斷地通過(guò) getEvent( ) 函數(shù)從 EventHub 中將事件取出并進(jìn)行處理。對(duì)于設(shè)備節(jié)點(diǎn)的增刪事件,將會(huì)更新輸入設(shè)備列表與配置。對(duì)于原始輸入事件,InputReader 對(duì)其進(jìn)行翻譯、組裝、封裝為包含更多信息、更具可讀性的輸入事件,然后交給 InputDispatcher 進(jìn)行派發(fā);
  • InputReaderPolicy:為 InputReader 的事件加工處理提供一些策略配置,例如鍵盤布局信息等;
  • InputDispatcherIMS 中的另一個(gè)關(guān)鍵組件,也運(yùn)行于一個(gè)獨(dú)立的線程中。InputDispatcher 中保管了來(lái)自 WindowManagerService 的所有窗口的信息,其收到來(lái)自 InputReader 的輸入事件后,會(huì)在其保管的窗口中尋找合適的窗口,并將事件派發(fā)給此窗口;
  • InputDispatcherPolicy:為 InputDispatcher 的派發(fā)過(guò)程提供策略控制。例如截取某些特定的輸入事件用作特殊用途,或者阻止將某些事件派發(fā)給目標(biāo)窗口。一個(gè)典型的例子就是 HOME 鍵InputDispatcherPolicy 截取到 PhoneWindowManager 中進(jìn)行處理,并阻止窗口收到 HOME 鍵按下的事件;
  • WindowManagerService:雖不是輸入系統(tǒng)中的成員,但卻對(duì) InputDispatcher 的正常工作起到了至關(guān)重要的作用。當(dāng)新建窗口時(shí),WMS 為新窗口和 IMS 之間創(chuàng)建了事件傳遞所用的通道。另外,WMS 還將所有窗口的信息,包括窗口的可點(diǎn)擊區(qū)域、焦點(diǎn)窗口等信息,實(shí)時(shí)地更新到 IMSInputDispatcher 中,使得 InputDispatcher 可以正確地將事件派發(fā)到指定的窗口;
  • ViewRootImpl:對(duì)某些窗口,如壁紙窗口、SurfaceView 的窗口來(lái)說(shuō),窗口就是輸入事件派發(fā)的終點(diǎn)。而對(duì)其他的如 Activity、對(duì)話框等使用了 Android 控件系統(tǒng)的窗口來(lái)說(shuō),輸入事件的終點(diǎn)是控件 View。ViewRootImpl 將窗口所接收的輸入事件沿著控件樹將事件派發(fā)給感興趣的控件 View

二、輸入系統(tǒng)相關(guān)源碼分析

我們知道,Zygote 進(jìn)程創(chuàng)建并啟動(dòng)后,在 fork 出的子進(jìn)程 SystemServer 的初始化過(guò)程中啟動(dòng) Android 系統(tǒng)所有的 Service 服務(wù),這些系統(tǒng)服務(wù)分為三大類:引導(dǎo)服務(wù)核心服務(wù)其他服務(wù),具體的啟動(dòng)流程可參考探索Framework之SystemServer進(jìn)程的啟動(dòng)詳解。

輸入系統(tǒng)服務(wù) IMS 是在啟動(dòng)其他服務(wù)里面啟動(dòng)的,接下來(lái)從源碼的角度來(lái)繼續(xù)探索分析。

1、IMS 構(gòu)建

SystemServer 類中找到啟動(dòng)其他服務(wù) startOtherServices() 方法的代碼,提取主要的邏輯代碼進(jìn)行分析,源碼如下:

1.1、SystemServer # startOtherServices()

xref: /frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer implements Dumpable {......private void startOtherServices(@NonNull TimingsTraceAndSlog t) {final Context context = mSystemContext;WindowManagerService wm = null;......InputManagerService inputManager = null;......try {......// 啟動(dòng) InputManagerService 服務(wù)t.traceBegin("StartInputManagerService"); // 新建 InputManagerService 對(duì)象inputManager = new InputManagerService(context);......t.traceBegin("StartWindowManagerService"); // 啟動(dòng) WindowManagerService 服務(wù)mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);// 使用新建的 IMS 對(duì)象來(lái)構(gòu)建 WMS 對(duì)象wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);......// 將 InputManagerService 發(fā)布到 ServiceManager 以便調(diào)用者可以訪問(wèn) IMS 提供的接口ServiceManager.addService(Context.INPUT_SERVICE, inputManager,/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);t.traceBegin("SetWindowManagerService"); // ActivityManagerService 設(shè)置 WindowManagerServicemActivityManagerService.setWindowManager(wm);t.traceBegin("StartInputManager"); // 設(shè)置向 WMS 發(fā)起回調(diào)的 callback 對(duì)象inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());inputManager.start(); // 啟動(dòng) InputManagerService,具體見......} catch (Throwable e) {......// 日志輸出并拋出異常}......final InputManagerService inputManagerF = inputManager;t.traceBegin("MakeInputManagerServiceReady");try {if (inputManagerF != null) {// 輸入系統(tǒng) IMS 準(zhǔn)備就緒inputManagerF.systemRunning();}} catch (Throwable e) {reportWtf("Notifying InputManagerService running", e);}......}......
}

IMS 的啟動(dòng)流程可以分為以下三個(gè)步驟:

  1. 構(gòu)建 IMS 實(shí)例對(duì)象,并建立上層與底層的映射關(guān)系。
  2. 啟動(dòng) IMS,其內(nèi)部就是啟動(dòng) native 層輸入系統(tǒng)的幾個(gè)重要參與者(后續(xù)會(huì)分析)。
  3. IMS 系統(tǒng)就緒,此時(shí) Java 層會(huì)同步一些配置給 native 層輸入系統(tǒng)。

首先是 IMS 實(shí)例對(duì)象的構(gòu)建,分析查看 IMS 類的源碼

1.2、InputManagerService

xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......private final Context mContext;private final InputManagerHandler mHandler;......private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);......public InputManagerService(Context context) {this.mContext = context;// 獲取 DisplayThread 的 Looper 創(chuàng)建 IMS 內(nèi)部的 InputManagerHandler 對(duì)象this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());mStaticAssociations = loadStaticInputPortAssociations();mUseDevInputEventForAudioJack =context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="+ mUseDevInputEventForAudioJack);// 每一個(gè)分為 Java 和 Native 兩部分的對(duì)象在創(chuàng)建時(shí)都會(huì)有一個(gè) native 函數(shù)// 在創(chuàng)建 Java 層對(duì)象的同時(shí) native 層也創(chuàng)建一個(gè),注意:使用的是同一個(gè) Looper 對(duì)象// mPtr 指向底層創(chuàng)建的 NativeInputManager 對(duì)象mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());String doubleTouchGestureEnablePath = context.getResources().getString(R.string.config_doubleTouchGestureEnableFile);mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :new File(doubleTouchGestureEnablePath);// 新建 IMS 的本地系統(tǒng)服務(wù) LocalService,其繼承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中LocalServices.addService(InputManagerInternal.class, new LocalService());}
}

方法中,獲取 DisplayThreadLooper,新建 InputManagerHandler 對(duì)象,然后調(diào)用 native 層的 nativeInit() 函數(shù),創(chuàng)建NativeInputManager 對(duì)象,最后新建 IMS 的本地系統(tǒng)服務(wù) LocalService,其繼承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中。

DisplayThreadsystem_server 進(jìn)程中是單例的,且只能被 WindowManager、DisplayManager、InputManager 使用。

1.3、NativeInputManager # nativeInit()

xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static const JNINativeMethod gInputManagerMethods[] = { // JNI 注冊(cè)的映射關(guān)系/* name, signature, funcPtr */{"nativeInit","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/""MessageQueue;)J",(void*)nativeInit},
};static jlong nativeInit(JNIEnv*env, jclass /* clazz */,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {// 由傳入的 Java 層的 MessageQueue 轉(zhuǎn)換獲取 native 層的 MessageQueuesp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == nullptr) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}// 新建 NativeInputManager 對(duì)象,此對(duì)象將是 native 層組件與 Java 層 IMS 進(jìn)行通信的橋梁NativeInputManager * im = new NativeInputManager(contextObj, serviceObj,messageQueue -> getLooper());im -> incStrong(0);// 返回指向 NativeInputManager 對(duì)象的指針給 Java 層的 IMS,IMS 將其保存在 mPtr 成員變量中return reinterpret_cast < jlong > (im);
}

通過(guò) JNI 注冊(cè)的映射關(guān)系,找到 native 層的 nativeInit() 函數(shù),首先由傳入的 Java 層的 MessageQueue 轉(zhuǎn)換獲取 native 層的 NativeMessageQueue 對(duì)象,然后新建 NativeInputManager 對(duì)象。

1.4、NativeInputManager

xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

class NativeInputManager : public virtual RefBase,public virtual InputReaderPolicyInterface,public virtual InputDispatcherPolicyInterface,public virtual PointerControllerPolicyInterface {public:NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }private:sp<InputManagerInterface> mInputManager;  jobject mServiceObj; // IMS 對(duì)象sp<Looper> mLooper; // Looper 對(duì)象Mutex mLock;struct Locked {......例如,mLocked.showTouches 是// 如果為 True,則啟用指針手勢(shì)bool pointerGesturesEnabled;// 由開發(fā)者選項(xiàng)中 "Show taps" 決定的,其功能是在屏幕上顯示一個(gè)觸摸點(diǎn)bool showTouches;......} mLocked GUARDED_BY(mLock);std::atomic<bool> mInteractive;......static inline JNIEnv* jniEnv() {return AndroidRuntime::getJNIEnv();}
};NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>&looper) :mLooper(looper),mInteractive(true) {JNIEnv * env = jniEnv();// 保存 Java 層的 InputManagerService 對(duì)象mServiceObj = env -> NewGlobalRef(serviceObj);{	// mLocked 的類型是 struct Locked,這里初始化了一些參數(shù),這些參數(shù)會(huì)被 Java 層改變AutoMutex _l (mLock);mLocked.systemUiLightsOut = false;mLocked.pointerSpeed = 0;mLocked.pointerGesturesEnabled = true;mLocked.showTouches = false;mLocked.pointerCapture = false;mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;}mInteractive = true;// 創(chuàng)建了 native 層的 InputManager,它才是底層輸入系統(tǒng)的服務(wù)InputManager * im = new InputManager(this, this);mInputManager = im;// 將 InputManager 注冊(cè)到 ServiceManager 中defaultServiceManager()->addService(String16("inputflinger"), im);
}
  1. NativeInputManager 的構(gòu)造函數(shù)中,創(chuàng)建一個(gè)全局引用,并通過(guò) mServiceObj 指向 Java 層的 IMS 對(duì)象,便于后續(xù)可以通過(guò) mServiceObj 調(diào)用 JavaIMS 對(duì)象的方法。
  2. 初始化參數(shù),這里要注意一個(gè)結(jié)構(gòu)體變量 mLocked,它的一些參數(shù)都是由 Java 層控制的。
  3. 然后將自己作為參數(shù)來(lái)新建 InputManager 對(duì)象,并將 InputManager 注冊(cè)到 ServiceManager 中,InputManager 才是 native 層輸入系統(tǒng)的服務(wù)。

注意:由 NativeInputManager 類的聲明可以看到,其實(shí)現(xiàn)了 InputReaderPolicyInterfaceInputDispatcherPolicyInterface 兩個(gè)接口。

1.5、InputManager

xref: /frameworks/native/services/inputflinger/InputManager.h

class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:~InputManager() override;public:InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);......
private:sp<InputReaderInterface> mReader;sp<InputClassifierInterface> mClassifier;sp<InputDispatcherInterface> mDispatcher;
};

xref: /frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {// 創(chuàng)建 InputDispatcher 對(duì)象,使用 InputDispatcherPolicyInterface 接口,用于對(duì)事件進(jìn)行分發(fā)mDispatcher = createInputDispatcher(dispatcherPolicy);// 創(chuàng)建 InputClassifier 對(duì)象,使用 InputListenerInterface,用于對(duì)事件分類mClassifier = new InputClassifier(mDispatcher);// 創(chuàng)建 InputReader 對(duì)象,使用 InputReaderPolicyInterface 和 InputListenerInterface// 其通過(guò) EventHub 監(jiān)聽"/dev/input"事件,獲取事件,然后把事件加工后,發(fā)送給 InputClassfiermReader = createInputReader(readerPolicy, mClassifier);
}

InputManager 內(nèi)部創(chuàng)建了三個(gè)子模塊:InputReader、InputClassifier、InputDispatcher,其作用如下:

  1. InputReader:負(fù)責(zé)從 EventHub 中獲取事件,然后把事件加工后,發(fā)送給 InputClassfier
  2. InputClassifer:負(fù)責(zé)把事件發(fā)送給 InputDispatcher,但是它會(huì)對(duì)觸摸事件進(jìn)行一個(gè)分類工作。
  3. InputDispatcher:對(duì)進(jìn)行事件分發(fā)。

此外,在上一小節(jié)的分析中,我們知道在構(gòu)建 InputManager 實(shí)例對(duì)象時(shí)使用了兩個(gè) this 參數(shù),而 InputManager 構(gòu)造函數(shù)需要的兩個(gè)接口參數(shù)正是由 NativeInputManager 實(shí)現(xiàn)的,而具體使用這兩個(gè)接口的不是 InputManager 自身,而是它內(nèi)部的子模塊 InputDispatcherInputReader。

InputDispatcherInputReader 在構(gòu)建時(shí)都傳遞了 NativeInputManager 對(duì)象參數(shù),并賦值到各自的 mPolicy 變量,后續(xù)可直接通過(guò) mPolicy 調(diào)用 JavaIMS 對(duì)象方法,因此 InputManagerJava 層通信的能力是由子模塊 InputDispatcherInputReader 實(shí)現(xiàn)的。

接下來(lái)首先來(lái)看看 InputDispatcher 是如何通過(guò) createInputDispatcher() 函數(shù)創(chuàng)建的,詳見接下來(lái)兩節(jié)的源碼分析。

1.6、InputDispatcher

xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp

sp<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {return new android::inputdispatcher::InputDispatcher(policy);
}

方法很簡(jiǎn)單,內(nèi)部直接新建 InputDispatcher 對(duì)象,再繼續(xù)查看 InputDispatcher 的構(gòu)造函數(shù):

xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h

class InputDispatcher : public android::InputDispatcherInterface {
protected:~InputDispatcher() override;public:explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
......
private:std::unique_ptr<InputThread> mThread;sp<InputDispatcherPolicyInterface> mPolicy;sp<Looper> mLooper;sp<InputReporterInterface> mReporter;
};

xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy): mPolicy(policy),mPendingEvent(nullptr),mLastDropReason(DropReason::NOT_DROPPED),mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),mAppSwitchSawKeyDown(false),mAppSwitchDueTime(LONG_LONG_MAX),mNextUnblockedEvent(nullptr),mDispatchEnabled(false),mDispatchFrozen(false),mInputFilterEnabled(false),// mInTouchMode will be initialized by the WindowManager to the default device config.// To avoid leaking stack in case that call never comes, and for tests,// initialize it here anyways.mInTouchMode(true),mMaximumObscuringOpacityForTouch(1.0f),mFocusedDisplayId(ADISPLAY_ID_DEFAULT),mFocusedWindowRequestedPointerCapture(false),mWindowTokenWithPointerCapture(nullptr),mLatencyAggregator(),mLatencyTracker(&mLatencyAggregator),mCompatService(getCompatService()) {mLooper = new Looper(false); // 新建自己的 Looper 對(duì)象mReporter = createInputReporter(); // 新建 InputReporter 對(duì)象mKeyRepeatState.lastKeyEntry = nullptr;policy->getDispatcherConfiguration(&mConfig);
}

在調(diào)用 InputDispatcher 的構(gòu)造函數(shù)構(gòu)建實(shí)例對(duì)象的同時(shí)將入?yún)?policy 賦值給 mPolicy 進(jìn)行保存 (這里入?yún)?policy 即是 NativeInputManager 對(duì)象)。其次新建自己的 Looper 對(duì)象,然后使用類似創(chuàng)建 InputDispatcher 的 createInputReporter() 函數(shù)新建 InputReporter 對(duì)象,代碼比較簡(jiǎn)單,不再深入追蹤,感興趣的可自行查看。

1.7、InputReader

接著來(lái)看看 InputReader 是如何通過(guò) createInputReader() 函數(shù)創(chuàng)建的,一起跟著源碼來(lái)學(xué)習(xí)。
xref: /frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp

sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {// 創(chuàng)建 EventHub 對(duì)象傳入到 InputReader 的構(gòu)造函數(shù)中來(lái)新建 InputReader 對(duì)象return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

該方法里面,在新建 InputReader 對(duì)象時(shí),結(jié)合 InputReader 類的構(gòu)造函數(shù)可知,第一個(gè)參數(shù)是 EventHub 的實(shí)例對(duì)象,那么 EventHub 對(duì)象是怎么創(chuàng)建的呢?

這里需要知道一些 C++ 有關(guān)的知識(shí),std::make_unique 的語(yǔ)法如下:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);

std::make_unique:是 C++11 標(biāo)準(zhǔn)引入的一個(gè)模版函數(shù),用于動(dòng)態(tài)分配指定類型的內(nèi)存,并返回一個(gè)指向分配內(nèi)存的唯一指針 (即 std::unique_ptr)。語(yǔ)法中,T 是指定的類型,Args 是可變長(zhǎng)模板參數(shù)包,用于傳遞給指定類型的構(gòu)造函數(shù)的參數(shù)。在調(diào)用 std::make_unique 時(shí),通過(guò) Args 包傳入構(gòu)造函數(shù)的參數(shù)會(huì)被轉(zhuǎn)發(fā)給類型 T 的構(gòu)造函數(shù),以生成相應(yīng)的對(duì)象實(shí)例。該函數(shù)返回的指針是一個(gè) std::unique_ptr 類型,表示一個(gè)擁有指向動(dòng)態(tài)內(nèi)存的所有權(quán)的對(duì)象。

xref: /frameworks/native/services/inputflinger/reader/include/InputReader.h

class InputReader : public InputReaderInterface {
public:InputReader(std::shared_ptr<EventHubInterface> eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener);virtual ~InputReader();
protected:// 在循環(huán)過(guò)程的每次迭代中,InputReader 讀取并處理一條來(lái)自 EventHub 的傳入消息void loopOnce();private:std::unique_ptr<InputThread> mThread;std::shared_ptr<EventHubInterface> mEventHub;sp<InputReaderPolicyInterface> mPolicy;sp<QueuedInputListener> mQueuedListener;
};

xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp

InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener): mContext(this),mEventHub(eventHub),mPolicy(policy),mGlobalMetaState(0),mLedMetaState(AMETA_NUM_LOCK_ON),mGeneration(1),mNextInputDeviceId(END_RESERVED_ID),mDisableVirtualKeysTimeout(LLONG_MIN),mNextTimeout(LLONG_MAX),mConfigurationChangesToRefresh(0) {mQueuedListener = new QueuedInputListener(listener);{ // acquire lockstd::scoped_lock _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}

與構(gòu)建 InputDispatcher 對(duì)象類似,在調(diào)用 InputReader 的構(gòu)造函數(shù)構(gòu)建實(shí)例對(duì)象的同時(shí)將入?yún)?policy 賦值給 mPolicyeventHub 對(duì)象賦值給 mEventHub 保存,同時(shí)新建 QueuedInputListener 監(jiān)聽對(duì)象。

通過(guò)前一小節(jié)的分析可知,EventHub 實(shí)例對(duì)象是通過(guò)調(diào)用 std::make_unique() 函數(shù)來(lái)創(chuàng)建的,那接下來(lái)一起去看看 EventHub 具體都做了些什么?

1.8、EventHub

xref: /frameworks/native/services/inputflinger/reader/include/EventHub.h

class EventHub : public EventHubInterface {
public:EventHub();
private:int32_t mNextDeviceId;BitSet32 mControllerNumbers;std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;std::vector<std::unique_ptr<Device>> mOpeningDevices;std::vector<std::unique_ptr<Device>> mClosingDevices;int mEpollFd;int mINotifyFd;int mWakeReadPipeFd;int mWakeWritePipeFd;int mInputWd;int mVideoWd;// 一次最多可處理的信號(hào)fd的數(shù)量static const int EPOLL_MAX_EVENTS = 16;// 掛起的 epoll 事件數(shù)組和下一個(gè)要處理的事件的索引struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];size_t mPendingEventCount;size_t mPendingEventIndex;bool mPendingINotify;
};

xref: /frameworks/native/services/inputflinger/reader/EventHub.cpp

EventHub::EventHub(void): mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),mNextDeviceId(1),mControllerNumbers(),mOpeningDevices(nullptr),mClosingDevices(nullptr),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {ensureProcessCanBlockSuspend();// 創(chuàng)建 Epoll 對(duì)象的描述符,監(jiān)聽設(shè)備節(jié)點(diǎn)是否有數(shù)據(jù)可讀(有無(wú)事件發(fā)生)mEpollFd = epoll_create1(EPOLL_CLOEXEC);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));// 創(chuàng)建 INotify 對(duì)象,用于監(jiān)聽設(shè)備節(jié)點(diǎn)的路徑 /dev/input,是否有變化,如有設(shè)備增刪則對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)的文件也會(huì)增刪mINotifyFd = inotify_init();// 添加 watch 監(jiān)聽存儲(chǔ)設(shè)備節(jié)點(diǎn)的路徑 DEVICE_PATH 的創(chuàng)建與刪除,當(dāng)有設(shè)備節(jié)點(diǎn)發(fā)生變化時(shí),通過(guò) INotify 對(duì)象可以讀取事件的詳細(xì)信息mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,strerror(errno));if (isV4lScanningEnabled()) {mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",VIDEO_DEVICE_PATH, strerror(errno));} else {mVideoWd = -1;ALOGI("Video device scanning disabled");}// 構(gòu)建 epoll_event 結(jié)構(gòu)體,并為每一個(gè)需要監(jiān)控的描述符填充該結(jié)構(gòu)體,以描述監(jiān)控事件struct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP; // 事件掩碼,指明需要監(jiān)聽的事件類型,可讀eventItem.data.fd = mINotifyFd; // 數(shù)據(jù)字段,設(shè)置需要監(jiān)聽的描述符,這里是 mINotifyFd,即監(jiān)聽設(shè)備節(jié)點(diǎn)的路徑// 調(diào)用 epoll_ctl() 函數(shù)將 INotify 對(duì)象注冊(cè)到 Epoll 中,監(jiān)聽其文件描述符對(duì)應(yīng)的文件夾下是否有設(shè)備節(jié)點(diǎn)的增刪信息// 第一個(gè)參數(shù)即前面創(chuàng)建的 Epoll 對(duì)象的描述符,第二個(gè)參數(shù)表示具體操作,這里 ADD 表示增加注冊(cè)事件// 第三個(gè)參數(shù)表示需要監(jiān)聽的描述符,第四個(gè)參數(shù)是描述監(jiān)聽事件的詳細(xì)信息的 epoll_event 結(jié)構(gòu)體int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);// 創(chuàng)建匿名管道 wakeFds,并將讀端交給 Epoll,寫端交給 InputReader,用于喚醒 Epoll,避免其阻塞在 epoll_wait()int wakeFds[ 2];result = pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);// mWakeReaderFD 和 mWakeWriterFD 對(duì)應(yīng)管道的兩端mWakeReadPipeFd = wakeFds[0];mWakeWritePipeFd = wakeFds[1];result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d", errno);result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d", errno);eventItem.data.fd = mWakeReadPipeFd;// epoll_ctl() 函數(shù)可重復(fù)調(diào)用,將多個(gè)文件描述符的多種事件監(jiān)聽注冊(cè)到 Epoll 對(duì)象中// 將匿名管道的讀取端的描述符也注冊(cè)到 Epoll 中,用于監(jiān)聽讀取端的可讀事件,當(dāng)寫入端有數(shù)據(jù)寫入時(shí)// 管道的讀取端就有數(shù)據(jù)可讀,使得 epoll_wait() 得以返回,從而達(dá)到喚醒 InputReader 線程的目的,避免其一直阻塞result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d", errno);
}

EventHub 的構(gòu)造函數(shù)主要工作有:

  1. 新建并初始化 Epoll、INotify 對(duì)象等;
  2. 調(diào)用 inotify_add_watch 函數(shù),監(jiān)聽 “/dev/input” 目錄下的設(shè)備節(jié)點(diǎn)創(chuàng)建與刪除操作,然后通過(guò) read() 函數(shù)讀取事件;
  3. INotify 添加到 Epoll 中,作為一個(gè)監(jiān)控對(duì)象;
  4. 創(chuàng)建管道,將管道讀取端的可讀事件添加到 Epoll 中,使 epoll_wait() 函數(shù)返回,喚醒 InputReader 線程來(lái)處理事件。

至此,IMSJava 層和 native 層的實(shí)例對(duì)象都已創(chuàng)建完成,并且在這個(gè)過(guò)程中,輸入系統(tǒng)的重要參與者也均創(chuàng)建完成。

1.9、小結(jié)

IMS的結(jié)構(gòu)體系
Java 層的 IMS 的主要工作是為 ReaderPolicyDispatcherPolicy 提供實(shí)現(xiàn),以及與 Android 其他系統(tǒng)服務(wù)進(jìn)行協(xié)作,其中最主要的協(xié)作者是 WMS。

NativeInputManager 位于 IMSJNI 層,負(fù)責(zé) Native 層的組件與 Java 層的 IMS 之間的相互通信。同時(shí)為 InputReaderInputDispatcher 提供了策略請(qǐng)求的接口。策略請(qǐng)求被他轉(zhuǎn)發(fā)給 Java 層的 IMS,由 IMS 進(jìn)行最終的決策定奪。

InputManagerInputReaderInputDispatcher 的運(yùn)行容器,在啟動(dòng) InputReaderInputDispatcher 時(shí),分別新建自己的運(yùn)行線程 InputThreadImpl 并啟動(dòng)運(yùn)行。

2、IMS 啟動(dòng)

在上一節(jié)的 SystemServer # startOtherServices() 方法中,在構(gòu)建完 IMS 后,IMS 系統(tǒng)中的各個(gè)重要參與者仍處于待命狀態(tài),需調(diào)用 IMS # start() 函數(shù)來(lái)啟動(dòng) IMS,繼續(xù)追蹤源碼分析:

2.1、IMS # start()

xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......private static native void nativeStart(long ptr);......public void start() {Slog.i(TAG, "Starting input manager");// 啟動(dòng) native 層的 IMSnativeStart(mPtr);Watchdog.getInstance().addMonitor(this);// 監(jiān)聽Settings.System.POINTER_SPEED,這個(gè)表示手指的速度registerPointerSpeedSettingObserver();// 監(jiān)聽Settings.System.SHOW_TOUCHES,這個(gè)表示是否在屏幕上顯示觸摸坐標(biāo)registerShowTouchesSettingObserver();registerAccessibilityLargePointerSettingObserver();registerLongPressTimeoutObserver();registerMaximumObscuringOpacityForTouchSettingObserver();registerBlockUntrustedTouchesModeSettingObserver();// 監(jiān)聽用戶切換mContext.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();updateDeepPressStatusFromSettings("user switched");}}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);// 從數(shù)據(jù)庫(kù)獲取值,并傳遞給 native 層updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();updateDeepPressStatusFromSettings("just booted");updateMaximumObscuringOpacityForTouchFromSettings();updateBlockUntrustedTouchesModeFromSettings();}......
}

IMS 的啟動(dòng)過(guò)程如下:

  1. 啟動(dòng) native 層輸入系統(tǒng),其實(shí)就是啟動(dòng)剛剛說(shuō)到的 InputReaderInputDispatcher。
  2. 注冊(cè)監(jiān)聽廣播,因?yàn)檫@些廣播與輸入系統(tǒng)的配置有關(guān),當(dāng)接收到這些廣播,會(huì)更新配置到 native 層。
  3. 直接讀取配置,更新到 native 層輸入系統(tǒng)。

2.2、NativeInputManager # nativeStart()

xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static const JNINativeMethod gInputManagerMethods[] = { // JNI 注冊(cè)的映射關(guān)系/* name, signature, funcPtr */{"nativeStart", "(J)V", (void*)nativeStart},
};static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {// 將 Java 層保存的 NativeInputManager 對(duì)象的指針轉(zhuǎn)換成 NativeInputManager 對(duì)象NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);// 查看 1.4 NativeInputManager 的源碼可知,獲取到 InputManager 對(duì)象,然后調(diào)用其 start() 函數(shù)status_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}

首先將 Java 層保存 NativeInputManager 對(duì)象的指針 mPtr 轉(zhuǎn)換成 NativeInputManager 對(duì)象,然后調(diào)用 NativeInputManager # getInputManager() 函數(shù)獲取到 InputManager 對(duì)象,接著調(diào)用 InputManager # start() 函數(shù)繼續(xù)啟動(dòng)流程。

reinterpret_cast 的功能可以分為兩類:1、指針和整數(shù)之間的轉(zhuǎn)換;2、不同類型的指針/成員指針/引用之間的轉(zhuǎn)換。

2.3、InputManager # start()

xref: /frameworks/native/services/inputflinger/InputManager.cpp

status_t InputManager::start() {// 啟動(dòng)承載 InputDispatcher 的線程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)單,調(diào)用 InputDispatcherInputReader 的 start() 函數(shù),啟動(dòng)承載它們運(yùn)行的線程,來(lái)看一下它們是如何啟動(dòng)線程的。

2.4、InputDispatcher # start()

xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}

在方法內(nèi),首先判斷 mThread 是否已存在,存在則直接返回,不存在則通過(guò) std::make_unique 函數(shù)來(lái)構(gòu)建 InputThread 的實(shí)例對(duì)象,但沒(méi)有看到啟動(dòng)線程的代碼邏輯,帶著這個(gè)疑問(wèn),我們?cè)偃タ纯?InputReader 的 start() 方法。

2.5、InputReader # start()

xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp

status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}

方法的代碼邏輯跟 InputDispatcher 的差不多,也是沒(méi)有看到啟動(dòng)線程的代碼邏輯,既然都是新建 InputThread 對(duì)象,那就具體來(lái)看一下 InputThread 類。

2.6、InputThread

xref: /frameworks/native/services/inputflinger/include/InputThread.h

class InputThread {
public:explicit InputThread(std::string name, std::function<void()> loop,std::function<void()> wake = nullptr);virtual ~InputThread();bool isCallingThread();
private:std::string mName; // 線程名std::function<void()> mThreadWake;sp<Thread> mThread; // 承載 InputDispatcher\InputReader 運(yùn)行的線程
};

xref: /frameworks/native/services/inputflinger/InputThread.cpp

class InputThreadImpl : public Thread {
public: // explicit 關(guān)鍵字的作用就是防止類構(gòu)造函數(shù)的隱式自動(dòng)轉(zhuǎn)換,且只對(duì)有一個(gè)參數(shù)的類構(gòu)造函數(shù)有效explicit InputThreadImpl(std::function<void()> loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::function<void()> mThreadLoop; // 存儲(chǔ)一個(gè)可調(diào)用對(duì)象,這里指的是 lambda 表達(dá)式bool threadLoop() override {mThreadLoop();return true;}
};InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {// 使用封裝的可調(diào)用對(duì)象 loop 新建 InputThreadImpl 對(duì)象mThread = new InputThreadImpl(loop);// 啟動(dòng) InputThreadImpl 線程mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

std::function:是一個(gè)通用的函數(shù)封裝類,它可以存儲(chǔ)、復(fù)制和調(diào)用任意可調(diào)用對(duì)象,包括函數(shù)指針、函數(shù)對(duì)象、成員函數(shù)指針和lambda 表達(dá)式等。通過(guò)使用 std::function 作為函數(shù)參數(shù),我們可以實(shí)現(xiàn)更加靈活的函數(shù)調(diào)用方式,提高代碼的可讀性和可維護(hù)性。

由代碼可知 InputThread 類其本身不是一個(gè)線程,其內(nèi)部是 InputThreadImpl 類來(lái)實(shí)現(xiàn)線程的具體功能。使用封裝的可調(diào)用對(duì)象 loop 構(gòu)建 InputThreadImpl 對(duì)象,然后調(diào)用其 run() 函數(shù)來(lái)啟動(dòng)線程。

InputThreadImpl 類繼承自 Thread 類,而 C++Thread 類提供了一個(gè)名為 threadLoop() 的純虛函數(shù),當(dāng)線程開始運(yùn)行后,將會(huì)在內(nèi)建的線程循環(huán)中不斷地調(diào)用 threadLoop() 函數(shù),直到此函數(shù)返回 false,則退出線程循環(huán)結(jié)束線程。但從 InputThreadImpl 類的定義可以看出,threadLoop() 函數(shù)會(huì)一直保持循環(huán)(返回值始終為 true),并且每一次循環(huán),會(huì)調(diào)用一次 mThreadLoop() 函數(shù),而 mThreadLoop() 函數(shù)是由 InputDispacherInputReader 在啟動(dòng)時(shí)封裝好傳入的可調(diào)用對(duì)象。

到這里,終于搞明白了,在 InputDispatcher 啟動(dòng)時(shí),會(huì)創(chuàng)建一個(gè)線程,然后循環(huán)調(diào)用 dispatchOnce() 函數(shù),同樣 InputReader 啟動(dòng)時(shí),也會(huì)創(chuàng)建一個(gè)線程,然后循環(huán)調(diào)用 loopOnce() 函數(shù)。

3、IMS 系統(tǒng)就緒

上面兩節(jié)已完成 IMS 及其重要參與者的構(gòu)建,并啟動(dòng)了 IMS 系統(tǒng),接下來(lái)就是通知系統(tǒng) IMS 系統(tǒng)已完成啟動(dòng)并準(zhǔn)備就緒,具體看一下源碼
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {......// IMS 系統(tǒng)內(nèi)部的 Handler,用來(lái)處理鍵盤等輸入設(shè)備有關(guān)的消息private final InputManagerHandler mHandler;private WiredAccessoryCallbacks mWiredAccessoryCallbacks; // 有線連接的設(shè)備回調(diào)private boolean mSystemReady; // 標(biāo)志系統(tǒng)是否準(zhǔn)備完畢private NotificationManager mNotificationManager; // 通知管理......public void systemRunning() {......mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);synchronized (mLidSwitchLock) {mSystemReady = true;......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)聽廣播,通知 native 層加載鍵盤布局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() {@Overridepublic void onReceive(Context context, Intent intent) {// 其內(nèi)部繼續(xù)調(diào)用 reloadKeyboardLayouts() 函數(shù)updateKeyboardLayouts();}}, filter, null, mHandler);// 監(jiān)聽廣播,通知 native 層加載設(shè)備別名filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);mContext.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {reloadDeviceAliases();}}, filter, null, mHandler);// 通過(guò) InputManagerHandler 發(fā)送消息來(lái)通知 native 層加載鍵盤布局和加載設(shè)備別名mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);// 如果與系統(tǒng)連接的有線設(shè)備的回調(diào)不為空,則須回調(diào)通知其輸入系統(tǒng) IMS 已準(zhǔn)備完畢if (mWiredAccessoryCallbacks != null) { mWiredAccessoryCallbacks.systemReady();}}private void reloadKeyboardLayouts() {if (DEBUG) {Slog.d(TAG, "Reloading keyboard layouts.");}// 調(diào)用 native 層函數(shù)來(lái)加載鍵盤布局nativeReloadKeyboardLayouts(mPtr);}private void reloadDeviceAliases() {if (DEBUG) {Slog.d(TAG, "Reloading device names.");}// 調(diào)用 native 層函數(shù)來(lái)加載設(shè)備別名nativeReloadDeviceAliases(mPtr);}......
}

注冊(cè)監(jiān)聽廣播,通知 native 層加載鍵盤布局、設(shè)備別名,最后通過(guò) JNI 調(diào)用 native 層的函數(shù)來(lái)加載鍵盤布局、設(shè)備別名。此外,如果與系統(tǒng)連接的有線設(shè)備注冊(cè)的回調(diào)不為空,則需回調(diào)通知其輸入系統(tǒng) IMS 已準(zhǔn)備就緒。


三、總結(jié)

1、IMS 啟動(dòng)時(shí)序圖

IMS 啟動(dòng)時(shí)序圖

2、IMS 成員關(guān)系圖

IMS 成員關(guān)系圖
最后結(jié)合 IMS 的啟動(dòng)時(shí)序圖和成員關(guān)系圖可以更深刻的理解 IMS 系統(tǒng)的構(gòu)成與啟動(dòng)過(guò)程,下一篇文章來(lái)繼續(xù)探索輸入系統(tǒng)的重要組成成員,等待后續(xù)吧!

參考

  1. 深入理解Android:卷III
http://www.risenshineclean.com/news/40746.html

相關(guān)文章:

  • php建設(shè)網(wǎng)站工具百度搜索推廣流程
  • 私人訂制寧波受歡迎全網(wǎng)seo優(yōu)化
  • 網(wǎng)站建設(shè)的前景長(zhǎng)沙優(yōu)化網(wǎng)站廠家
  • 建設(shè)網(wǎng)站需要哪些流程百度seo費(fèi)用
  • 制造業(yè)人才網(wǎng)正規(guī)seo排名多少錢
  • 公眾平臺(tái)注冊(cè)網(wǎng)站怎么優(yōu)化搜索
  • 網(wǎng)站icp備案 年檢2345網(wǎng)址導(dǎo)航中國(guó)最好
  • 如何開發(fā)一個(gè)app建設(shè)一個(gè)網(wǎng)站關(guān)鍵詞搜索熱度查詢
  • 廣西搜索推廣東莞網(wǎng)絡(luò)優(yōu)化排名
  • 網(wǎng)站IcP在哪查今日剛剛發(fā)生的國(guó)際新聞
  • 哈爾濱網(wǎng)站建設(shè)1元錢2021年經(jīng)典營(yíng)銷案例
  • 做企業(yè)網(wǎng)站的第一步需要啥紹興seo優(yōu)化
  • 四川seo整站優(yōu)化吧谷歌瀏覽器官方app下載
  • 國(guó)外做美食視頻網(wǎng)站谷歌海外推廣怎么做
  • 淄博網(wǎng)站建設(shè)推廣優(yōu)化自媒體賬號(hào)申請(qǐng)
  • 網(wǎng)站制作系統(tǒng)長(zhǎng)沙官網(wǎng)seo技術(shù)廠家
  • 黃石規(guī)劃建設(shè)局網(wǎng)站一鍵優(yōu)化清理手機(jī)
  • 做一個(gè)網(wǎng)上商城網(wǎng)站建設(shè)費(fèi)用多少錢市場(chǎng)調(diào)研分析報(bào)告范文
  • 網(wǎng)站開發(fā)素材包網(wǎng)站的宣傳與推廣
  • 網(wǎng)站建設(shè)后臺(tái)管理怎么進(jìn)入烏魯木齊seo
  • 商城網(wǎng)站模版代碼重慶seo整站優(yōu)化方案范文
  • 開發(fā)動(dòng)態(tài)網(wǎng)站有哪些技術(shù)百度人工客服電話24小時(shí)
  • 網(wǎng)站開發(fā)全包免費(fèi)手機(jī)優(yōu)化大師下載安裝
  • 外貿(mào)品牌網(wǎng)站設(shè)計(jì)公司鼓樓網(wǎng)頁(yè)seo搜索引擎優(yōu)化
  • 網(wǎng)站開發(fā)需求列表2021最火營(yíng)銷方案
  • 怎么可以自己做網(wǎng)站被百度收到網(wǎng)站seo公司哪家好
  • 石家莊網(wǎng)站建設(shè)價(jià)格低廣州今日新聞?lì)^條新聞
  • 表白網(wǎng)站怎樣做有創(chuàng)意品牌推廣活動(dòng)策劃方案
  • 公路建設(shè)管理辦公室網(wǎng)站中國(guó)最好的營(yíng)銷策劃公司
  • 石家莊做網(wǎng)站100個(gè)商業(yè)經(jīng)典案例