Android觸摸事件的傳遞(二)--輸入系統(tǒng)InputManagerService

了解更多敦捧,移步Android觸摸事件傳遞機制系列詳解

1 概述

  1. 當(dāng)用戶觸摸屏幕或者按鍵操作刘陶,首次觸發(fā)的是硬件驅(qū)動动壤,驅(qū)動收到事件后锅风,將該相應(yīng)事件寫入到輸入設(shè)備節(jié)點酥诽, 這便產(chǎn)生了最原生態(tài)的內(nèi)核事件。
  2. 接著遏弱,輸入系統(tǒng)取出原生態(tài)的事件盆均,經(jīng)過層層封裝后成為KeyEvent或者MotionEvent
  3. 最后漱逸,交付給相應(yīng)的目標(biāo)窗口(Window)來消費該輸入事件泪姨。可見饰抒,輸入系統(tǒng)在整個過程起到承上啟下的銜接作用肮砾。

2 Input模塊的主要組成:

  1. Native層的InputReader負責(zé)從EventHub取出事件并處理,再交給InputDispatcher袋坑;
  2. Native層的InputDispatcher接收來自InputReader的輸入事件仗处,并記錄WMS的窗口信息,用于派發(fā)事件到合適的窗口
  3. Java層的InputManagerService跟WMS交互婆誓,WMS記錄所有窗口信息吃环,并同步更新到IMS,為InputDispatcher正確派發(fā)事件到ViewRootImpl提供保障洋幻;

3 整體框架類圖

InputManagerService作為system_server中的重要服務(wù)郁轻,繼承于IInputManager.Stub, 作為Binder服務(wù)端文留,那么Client位于InputManager的內(nèi)部通過IInputManager.Stub.asInterface()獲取Binder代理端好唯,C/S兩端通信的協(xié)議是由IInputManager.aidl來定義的。

input_binder.jpg

圖解:

  • InputManagerService位于Java層的InputManagerService.java文件燥翅;
    a. 其成員mPtr指向Native層的NativeInputManager對象骑篙;
  • NativeInputManager位于Native層的com_android_server_input_InputManagerService.cpp文件;
    a. 其成員mServiceObj指向Java層的IMS對象森书;
    b. 其成員mLooper是指“android.display”線程的Looper;
  • InputManager位于libinputflinger中的InputManager.cpp文件靶端;
    a. InputDispatcherInputReader的成員變量mPolicy都是指NativeInputManager對象;
    b. InputReader的成員mQueuedListener,數(shù)據(jù)類型為QueuedInputListener凛膏;通過其內(nèi)部成員變量mInnerListener指向InputDispatcher對象躲查; 這便是InputReaderInputDispatcher交互的中間樞紐。

4 啟動調(diào)用棧(流程)

IMS服務(wù)是伴隨著system_server進程的啟動而啟動译柏,整個調(diào)用過程:

InputManagerService(初始化)
    nativeInit
        NativeInputManager
            EventHub
            InputManager
                InputDispatcher
                    Looper
                InputReader
                    QueuedInputListener
                InputReaderThread
                InputDispatcherThread
IMS.start(啟動)
    nativeStart
        InputManager.start
            InputReaderThread->run
            InputDispatcherThread->run

整個過程首先創(chuàng)建如下對象:NativeInputManagerEventHub姐霍,InputManager鄙麦, InputDispatcherInputReader镊折,InputReaderThread胯府,InputDispatcherThread。 接著便是啟動兩個工作線程InputReader,InputDispatcher恨胚。

5 IMS啟動過程

private void startOtherServices() {
    //1. 初始化IMS對象
    inputManager = new InputManagerService(context);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    ...
    //將InputMonitor對象保持到IMS對象
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    //2
    inputManager.start();
}

通過上述代碼接下骂因,分InputManagerService初始化和InputManagerService的啟動來寫

6 InputManagerService初始化

6.1 構(gòu)造方法

創(chuàng)建InputManagerService對象--構(gòu)造方法:[-> InputManagerService.java]

public InputManagerService(Context context) {
     this.mContext = context;
     // 運行在線程"android.display"
     this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
     ...

     //初始化native對象
     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
     LocalServices.addService(InputManagerInternal.class, new LocalService());
 }

6.2 構(gòu)造方法中調(diào)用nativeInit

[-> com_android_server_input_InputManagerService.cpp]

static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    //獲取native消息隊列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...
    //創(chuàng)建Native的InputManager【見小節(jié)2.3】
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im); //返回Native對象的指針
}

6.3 nativeInit中創(chuàng)建NativeInputManager

[-> 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); //上層IMS的context
    mServiceObj = env->NewGlobalRef(serviceObj); //上層IMS對象
    ...
    sp<EventHub> eventHub = new EventHub(); // 創(chuàng)建EventHub對象【見小節(jié)2.4】
    mInputManager = new InputManager(eventHub, this, this); // 創(chuàng)建InputManager對象
}

6.4 NativeInputManager中創(chuàng)建EventHub

[-> 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);
    //創(chuàng)建epoll
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    mINotifyFd = inotify_init();
    //此處DEVICE_PATH為"/dev/input"赃泡,監(jiān)聽該設(shè)備路徑
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    //添加INotify到epoll實例
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    int wakeFds[2];
    result = pipe(wakeFds); //創(chuàng)建管道

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    //將pipe的讀和寫都設(shè)置為非阻塞方式
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

    eventItem.data.u32 = EPOLL_ID_WAKE;
    //添加管道的讀端到epoll實例
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ...
}

該方法主要功能:

  • 初始化INotify(監(jiān)聽”/dev/input”)寒波,并添加到epoll實例
  • 創(chuàng)建非阻塞模式的管道,并添加到epoll;

6.5 NativeInputManager中創(chuàng)建InputManager

[-> InputManager.cpp]

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //創(chuàng)建InputDispatcher對象
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    //創(chuàng)建InputReader對象
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputDispatcherInputReadermPolicy成員變量都是指NativeInputManager對象升熊。

6.6 InputManager中創(chuàng)建InputDispatcher

[-> InputDispatcher.cpp]

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
    mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    //創(chuàng)建Looper對象
    mLooper = new Looper(false);

    mKeyRepeatState.lastKeyEntry = NULL;
    //獲取分發(fā)超時參數(shù)
    policy->getDispatcherConfiguration(&mConfig);
}

該方法主要工作:

  • 創(chuàng)建屬于自己線程的Looper對象俄烁;
  • 超時參數(shù)來自于IMS,參數(shù)默認值keyRepeatTimeout= 500级野,keyRepeatDelay = 50页屠。

6.7 InputManager中創(chuàng)建InputReader

[-> InputReader.cpp]

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 創(chuàng)建輸入監(jiān)聽對象
    mQueuedListener = new QueuedInputListener(listener);
    {
        AutoMutex _l(mLock);
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    }
}

此處mQueuedListener的成員變量mInnerListener便是InputDispatcher對象。 InputManager創(chuàng)建完InputDispatcherInputReader對象, 接下里便是調(diào)用initialize初始化辰企。

6.8 InputManager中初始化initialize

InputManager創(chuàng)建完InputDispatcher和InputReader對象风纠, 接下里便是調(diào)用initialize初始化。
[-> InputManager.cpp]

void InputManager::initialize() {
    //創(chuàng)建線程“InputReader”
    mReaderThread = new InputReaderThread(mReader);
    //創(chuàng)建線程”InputDispatcher“
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
        Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

初始化的主要工作就是創(chuàng)建兩個能訪問Java代碼的native線程牢贸。

  • 創(chuàng)建線程“InputReader”
  • 創(chuàng)建線程”InputDispatcher“

整個的InputManagerService對象初始化過程并完成竹观,接下來便是調(diào)用其start方法。

7 IMS.start

[-> InputManagerService.java]

public void start() {
    // 啟動native對象[見小節(jié)2.10]
    nativeStart(mPtr);

    Watchdog.getInstance().addMonitor(this);

    //注冊觸摸點速度和是否顯示功能的觀察者
    registerPointerSpeedSettingObserver();
    registerShowTouchesSettingObserver();

    mContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updatePointerSpeedFromSettings();
            updateShowTouchesFromSettings();
        }
    }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);

    updatePointerSpeedFromSettings(); //更新觸摸點的速度
    updateShowTouchesFromSettings(); //是否在屏幕上顯示觸摸點
}

7.1 start中調(diào)用nativeStart

[-> com_android_server_input_InputManagerService.cpp]

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    //此處ptr記錄的便是NativeInputManager
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // [見小節(jié)7.2]
    status_t result = im->getInputManager()->start();
    ...
}

7.2 InputManager.start

[InputManager.cpp]

status_t InputManager::start() {
    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ...
    return OK;
}

該方法的主要功能是啟動兩個線程:

  • 啟動線程“InputReader”
  • 啟動線程”InputDispatcher“

8 總結(jié)

分層視角:

  1. Java層InputManagerService:采用android.display線程處理Message.
  2. JNI的NativeInputManager:采用android.display線程處理Message,以及創(chuàng)建EventHub十减。
  3. Native的InputManager:創(chuàng)建InputReaderThreadInputDispatcherThread兩個線程

主要功能:

  • IMS服務(wù)中的成員變量mPtr記錄Native層的NativeInputManager對象栈幸;
  • IMS對象的初始化過程的重點在于native初始化,分別創(chuàng)建了以下對象:
    NativeInputManager帮辟;
    EventHub, InputManager速址;
    InputReaderInputDispatcher由驹;
    InputReaderThread芍锚,InputDispatcherThread
  • IMS啟動過程的主要功能是啟動以下兩個線程:
    InputReader:從EventHub取出事件并處理,再交給InputDispatcher
    InputDispatcher:接收來自InputReader的輸入事件蔓榄,并派發(fā)事件到合適的窗口并炮。

從整個啟動過程,可知有system_server進程中有3個線程跟Input輸入系統(tǒng)息息相關(guān)甥郑,分別是android.display, InputReader,InputDispatcher逃魄。

  • InputDispatcher線程:屬于Looper線程,會創(chuàng)建屬于自己的Looper澜搅,循環(huán)分發(fā)消息伍俘;
  • InputReader線程:通過getEvents()調(diào)用EventHub讀取輸入事件,循環(huán)讀取消息勉躺;
  • android.display線程:屬于Looper線程癌瘾,用于處理Java層的IMS.InputManagerHandler和JNI層的NativeInputManager中指定的MessageHandler消息;
事件分發(fā)1.png

參考

Android系統(tǒng)源碼分析-事件收集
Android 輸入系統(tǒng)(一)InputManagerService
Input系統(tǒng)—啟動篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市饵溅,隨后出現(xiàn)的幾起案子妨退,更是在濱河造成了極大的恐慌,老刑警劉巖蜕企,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咬荷,死亡現(xiàn)場離奇詭異,居然都是意外死亡糖赔,警方通過查閱死者的電腦和手機萍丐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門晓铆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饿敲,“玉大人,你說我怎么就攤上這事膛堤。” “怎么了壳影?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵拱层,是天一觀的道長。 經(jīng)常有香客問我宴咧,道長根灯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任掺栅,我火速辦了婚禮烙肺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氧卧。我一直安慰自己桃笙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布沙绝。 她就那樣靜靜地躺著搏明,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闪檬。 梳的紋絲不亂的頭發(fā)上星著,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音粗悯,去河邊找鬼虚循。 笑死,一個胖子當(dāng)著我的面吹牛样傍,可吹牛的內(nèi)容都是我干的邮丰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼铭乾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了娃循?” 一聲冷哼從身側(cè)響起炕檩,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捌斧,沒想到半個月后笛质,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡捞蚂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年妇押,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姓迅。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡敲霍,死狀恐怖俊马,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肩杈,我是刑警寧澤柴我,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站扩然,受9級特大地震影響艘儒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夫偶,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一界睁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兵拢,春花似錦翻斟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至截汪,卻和暖如春疾牲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衙解。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工阳柔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚓峦。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓舌剂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親暑椰。 傳聞我的和親對象是個殘疾皇子霍转,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,777評論 25 707
  • 沉悶的空氣,像陰暗的囚籠 靜謐的氛圍一汽,像審訊的牢房 人們都低沉著原本高傲的頭顱避消,向著罪惡的王冠屈服。 原本跳動的心...
    流年已盡閱讀 531評論 3 2
  • 我們都是赤條條的來到這個世迄委,是從什么時候開始,人與人之間有了那么多的天差地別腾它?~ 不得不說跑筝,當(dāng)我們第一聲啼哭后,被...
    minnameng閱讀 242評論 0 2
  • 今年已經(jīng)88歲的公公,依然堅持每天寫字一小時妓忍,開始再拿起毛筆寫字虏两,是在公公退休幾年后的事兒,從此斷斷續(xù)續(xù)二十余年世剖,...
    灰玫瑰汪然閱讀 153評論 0 3