該系列文章總綱連結:專題分綱目錄 Android Framework 輸入子系統
本章關鍵點總結 & 說明:
本章節 導圖主要關注➕ IMS 架構部分即可,通過啟動過程的分析捋清楚 輸入子系統的架構:就是一個從kernel中斷,産生事件,/dev/input目錄下檔案變化,到讀取資料,再到派發資料,最後到處理資料這樣完整的流程。
1 IMS(InputManagerService)簡介
下面這張圖(《深入了解Android 卷III》)很清楚的描述了輸入子系統的整體架構:
說明:輸入事件的源頭是位于/dev/input/下的裝置節點,輸入系統的終點是由WMS管理的某個視窗。Android輸入系統的主要工作是讀取裝置節點中原始事件,加工封裝後派發給一個特定的視窗以及視窗中的控件。接下來針對圖中幾個子產品進行說明:
子產品 | 簡介說明 |
Linux核心 | 接受輸入裝置中斷,将原始事件資料寫入到裝置節點 |
裝置節點 | IMS可以從中讀取事件 |
InputManagerService | 分為Java層和Native層兩部分,Java層負責與WMS的通信;Native層是InputReader和InputDispatcher兩個輸入系統關鍵元件的運作容器。 |
EventHub | 直接通路所有的裝置節點。通過一個名為getEvents()的函數将所有輸入系統相關的待處理底層事件傳回給使用者。 |
InputReader | 運作于獨立線程,負責管理輸入裝置的清單與配置,以及進行輸入事件的加工處理。通過線程循環不斷地通過getEvents()函數從EventHub中将事件取出并進行處理。對于裝置節點的增删事件,它會更新輸入裝置清單于配置。對于原始輸入事件,InputReader對其進行翻譯、組裝、封裝為包含了更多資訊、更具可讀性的輸入事件,然後交給InputDispatcher進行派發。 |
InputReaderPolicy | 為InputReader事件加工處理提供政策配置。 |
InputDispatcher | 是IMS中的另一個關鍵元件。它也運作于一個獨立的線程中。InputDispatcher中保管了來自WMS的所有視窗的資訊,收到來自InputReader的輸入事件後,會尋找合适的視窗,将事件派發給它。 |
InputDispatcherPolicy | 它為InputDispatcher的派發過程提供政策控制。 |
WMS | 對InputDispatcher的正常工作起到了至關重要的作用。建立視窗時,WMS為新視窗和IMS建立了事件傳遞所用的通道。另外,WMS還将所有視窗的資訊,包括視窗的可點選區域,焦點視窗等資訊,實時地更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派發到指定的視窗。 |
ViewRootImpl | 對于某些視窗,如桌面視窗、SurfaceView的視窗來說,視窗即是輸入事件派發的終點。而對于其他的如Activity、對話框等使用了Android控件系統的視窗來說,輸入事件的終點是控件(View)。ViewRootImpl将視窗所接收到的輸入事件沿着控件樹将事件派發給對應的控件。 |
流程總結:核心将原始事件寫入到裝置節點中,InputReader不斷地通過EventHub将原始事件取出來并加工成Android輸入事件,交給InputDispatcher。InputDispatcher根據WMS提供的視窗資訊将事件交給合适的視窗。視窗的ViewRootImpl對象再沿着控件樹将事件派發給感興趣的控件。控件對其收到的事件作出響應,更新自己的畫面、執行特定的動作。
2 IMS(InputManagerService)啟動流程分析
Android的輸入子系統是在InputManagerService中啟動的,而InputManagerService是在system_server中啟動的。接下來從SystemServer的啟動開始逐漸分析:
//systemserver->startOtherServices
private void startOtherServices() {
//...
inputManager = new InputManagerService(context);//關鍵點1
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();//關鍵點2
//...
}
從這裡開始 關注 ➕InputManagerService的構造器和線程啟動執行兩個部分。
2.1 InputManagerService構造器深入分析
構造器代碼如下:
public 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);
//調用nativeInit來執行C++層的初始化操作
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
這裡關注nativeInit的實作,代碼如下:(對應檔案 com_android_server_input_InputManagerService.cpp)
static jlong nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
//構造一個NativeInputManagera對象
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
這裡建立一個NativeInputManager的執行個體,并将其作為傳回值儲存在InputManagerService 中的mPtr字段裡,繼續分析NativeInputManager的構造器,代碼如下:
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;
}
//構造一個EventHub對象,傳遞給InputManager
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
這裡繼續分析關鍵對象InputManager,代碼如下:
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();
}
這裡建立了InputDispatcher對象 用于分發輸入事件,同時建立了一個InputReader對象 用于從EventHub中讀取輸入事件。同時這裡繼續分析initialize方法,代碼如下:
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
這裡建立了一個InputReaderThread和InputDispatcherThread對象,前面構造器中建立的InputReader是通過InputReaderThread來讀取輸入事件,而InputDispatcher實際通過InputDispatcherThread來分發輸入事件。下圖(《深入了解Android 卷III》)很清楚描述了輸入子系統的整體架構,如下所示:
InputManager的建立過程分别為InputReader與InputDispatcher建立了線程,IMS的各成員準備就緒。
2.2 InputManagerService線程啟動分析
線程啟動是從這裡開始:
inputManager.start();
繼續分析,代碼如下:
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
//...
}
繼續分析nativeStart,代碼如下:
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
}
實際調用了InputManager的start函數,繼續分析,代碼如下:
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
//...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
//...
return OK;
}
InputManager啟動了一個InputReaderThread和InputDispatcherThread來讀取和分發輸入消息,調用它們的run方法後,會進入threadLoop函數(隻要threadLoop函數傳回true,該函數就會循環執行)。後面章節 會單獨從讀取事件、派發事件角度來解讀兩個線程。
這裡start()函數的功能就是啟動這兩個線程,使得InputReader于InputDispatcher開始工作。當兩個線程啟動後,InputReader在其線程循環中不斷地從EventHub中讀取輸入事件,加工處理後将事件放入InputDispatcher的派發發隊列中,InputDispatcher線上程循環中将派發隊列中的事件取出,查找合适視窗,将事件寫入。視窗事件接收線程的Looper從管道中将事件取出,交由事件處理函數進行事件響應。過程如下圖所示:(《深入了解Android 卷III》)
3 總結 IMS各成員關系(《深入了解Android 卷III》)
該圖很好的诠釋了IMS相關類的 成員關系,左邊是Reader子系統,右邊是 Dispatcher子系統。而接下來的兩節主要針對兩個子系統Reader和Dispatcher進行更加詳細的分析。