本文首發於微信公眾號「劉望舒」
原文連結 : Android輸入系統的事件傳遞流程和IMS的誕生
前言
很多同學可能會認為輸入系統是不是和View的事件分發有些關聯,確實是有些關聯,只不過View事件分發只能算是輸入系統事件傳遞的一部分。這個系列講的輸入系統主要是我們不常接觸的,但還是需要去了解的那部分。
1. 輸入事件傳遞流程的組成部分
輸入系統是外界與Android裝置互動的基礎,僅憑輸入系統是無法完成輸入事件傳遞的,因此需要輸入系統和Android系統的其他成員來共同完成事件傳遞。輸入系統事件傳遞需要經過以下幾個部分。
輸入事件傳遞流程可以大致的分為三個部分,分別是輸入系統部分、WMS處理部分和View處理部分。下面分別對這幾個部分進行簡單的介紹。
輸入系統部分
輸入系統部分主要又分為輸入子系統和InputManagerService組成(以下簡稱IMS),在Android中還有一個IMS(IP Multimedia Subsystem)意為為IP多媒體子系統,不要搞混了。 Android的輸入裝置有很多種,比如螢幕、鍵盤、滑鼠、遊戲手柄、操縱桿等等,其中應用開發接觸最多的螢幕。當輸入裝置可用時,Linux核心會在/dev/input中建立對應的裝置節點。 使用者操作這些輸入裝置時會產生各種事件比如按鍵事件、觸控事件、滑鼠事件等。 輸入事件所產生的原始資訊會被Linux核心中的輸入子系統採集,原始資訊由Kernel space的驅動層一直傳遞到User space的裝置節點。
Android提供了getevent和sendevent兩個工具幫助開發者從裝置節點讀取輸入事件和寫入輸入事件。
IMS所做的工作就是監聽/dev/input下的所有的裝置節點,當裝置節點有資料時會將資料進行加工處理並找到合適的Window,將輸入事件派發給它。
WMS處理部分
在Android解析WindowManagerService(一)WMS的誕生這篇文章中我講過WMS的職責有四種,如下圖所示。
WMS的職責之一就是輸入系統的中轉站,WMS作為Window的管理者,會配合IMS將輸入事件交由合適的Window來處理。
View處理部分
View處理部分應該是大家最熟悉的了,一般情況下,輸入事件最終會交由View來處理,應用開發者就可以通過一些回撥方法輕鬆得到這個事件的封裝類並對其進行處理,比如onTouchEvent(MotionEvent ev)方法。關於View體系可以檢視View體系這一系列文章。
2. IMS的誕生
輸入事件傳遞流程的組成部分我們已經瞭解了,本系列主要講解輸入系統部分中IMS對輸入事件的處理,在這之前我們需要了解IMS的誕生。
2.1 SyetemServer處理部分
與AMS、WMS、PMS一樣,IMS的在SyetemServer程式中被建立的,SyetemServer程式用來建立系統服務,不瞭解它的可以檢視 Android系統啟動流程(三)解析SyetemServer程式啟動過程 這篇文章。 從SyetemServer的入口方法main方法開始講起,如下所示。 frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
複製程式碼
main方法中只呼叫了SystemServer的run方法,如下所示。 frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
...
try {
traceBeginAndSlog("StartServices");
//啟動引導服務
startBootstrapServices();//1
//啟動核心服務
startCoreServices();//2
//啟動其他服務
startOtherServices();//3
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
...
}
複製程式碼
在註釋1中的startBootstrapServices方法中用SystemServiceManager啟動了ActivityManagerService、PowerManagerService、PackageManagerService等服務。在註釋2處的startCoreServices方法中則啟動了DropBoxManagerService、BatteryService、UsageStatsService和WebViewUpdateService。註釋3處的startOtherServices方法中啟動了CameraService、AlarmManagerService、VrManagerService等服務。這些服務的父類均為SystemService。從註釋1、2、3的方法可以看出,官方把系統服務分為了三種型別,分別是引導服務、核心服務和其他服務,其中其他服務是一些非緊要和一些不需要立即啟動的服務。這些系統服務總共有100多個,我們熟知的AMS和PMS屬於引導服務,WMS屬於其他服務。 本文要講的IMS屬於其他服務,這裡列出其他服務以及它們的作用,見下表。
其他服務 | 作用 |
---|---|
CameraService | 攝像頭相關服務 |
AlarmManagerService | 全域性定時器管理服務 |
InputManagerService | 管理輸入事件 |
WindowManagerService | 視窗管理服務 |
VrManagerService | VR模式管理服務 |
BluetoothService | 藍芽管理服務 |
NotificationManagerService | 通知管理服務 |
DeviceStorageMonitorService | 儲存相關管理服務 |
LocationManagerService | 定位管理服務 |
AudioService | 音訊相關管理服務 |
... | .... |
檢視啟動其他服務的註釋3處的startOtherServices方法。 frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
...
inputManager = new InputManagerService(context);//1
traceEnd();
traceBeginAndSlog("StartWindowManagerService");
// WMS needs sensor service ready
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());//2
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
traceEnd();
...
}
複製程式碼
註釋1處建立了IMS,註釋2處執行了WMS的main方法,其內部會建立WMS。需要注意的是,main方法的其中一個引數就是註釋1處建立的IMS,在本地第1節中我們知道WMS是輸入系統的中轉站,其內部包含了IMS引用並不意外。緊接著將WMS和IMS新增到ServiceManager中進行統一的管理。
2.2 InputManagerService構造方法
我們接著來檢視IMS的構造方法。 frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//1
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//2
...
}
複製程式碼
註釋1處用android.display執行緒的Looper建立了InputManagerHandler,這樣InputManagerHandler會執行在android.display執行緒,android.display執行緒是系統共享的單例前臺執行緒,這個執行緒內部執行了WMS的建立,具體見 Android解析WindowManagerService(一)WMS的誕生這篇文章。 註釋2處呼叫了nativeInit方法,很明顯是要通過JNI呼叫Navive方法。 frameworks/base/services/core/jni/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;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());//1
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
複製程式碼
註釋1處建立了NativeInputManager,最後會呼叫reinterpret_cast運算子將NativeInputManager指標強制轉換並返回(重新解釋位元位)。NativeInputManager的建構函式如下所示。 frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
...
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
複製程式碼
NativeInputManager建構函式中建立了EventHub和InputManager,EventHub通過Linux核心的INotify與Epoll機制監聽裝置節點,通過EventHub的getEvent函式讀取裝置節點的增刪事件和原始輸入事件,本系列後續文章會詳細介紹EventHub。InputManager的建構函式如下所示。 frameworks/native/services/inputflinger/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);
}
複製程式碼
InputManager建構函式中建立了InputReader和InputDispatcher,InputReader會不斷迴圈讀取EventHub中的原始輸入事件,將這些原始輸入事件進行加工後交由InputDispatcher,InputDispatcher中儲存了WMS中的所有Window資訊(WMS會將視窗的資訊實時的更新到InputDispatcher中),這樣InputDispatcher就可以將輸入事件派發給合適的Window。InputReader和InputDispatcher都是耗時操作,因此在initialize函式中建立了供它們執行的執行緒InputReaderThread和InputDispatcherThread。 InputManagerService構造方法描繪瞭如下的IMS簡圖。
從上面的簡圖可以看出來,IMS主要的工作都在Native層中,這些內容會在本系列的後續文章進行介紹。
感謝
《深入理解Android》卷3
《深入理解Android核心設計思想》
blog.csdn.net/u013604527/…
www.cnblogs.com/deng-tao/p/…
分享大前端、Java和前沿技術,關注職業發展和行業動態。