framework 學(xué)習(xí)筆記20. input輸入事件番外3(native層IMS的啟動(dòng))

1. SystemServer 中啟動(dòng) IMS

上一章節(jié)中,介紹了輸入事件的整體架構(gòu)設(shè)計(jì)和 java 層 InputManagerService 的啟動(dòng)涯雅,接下來將從源碼中詳細(xì)跟蹤 IMS 的啟動(dòng)鲜结;首先從 SystemServer.java 中 startOtherServices() 方法開始:

  private void startOtherServices() {
      // 創(chuàng)建 IMS
      inputManager = new InputManagerService(context); // 見1.2
      // 創(chuàng)建 WMS
      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);

      mActivityManagerService.setWindowManager(wm);
      // wm.getInputMonitor() 獲取到的是InputMonitor 
      inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); // IMS 和 WMS 相互關(guān)聯(lián) 見 1.1
      inputManager.start();  // 見1.2
  }

1.1 InputMonitor:通信橋梁的建立 (InputMonitor 是 IMS 和 WMS 之間的紐帶,在是在 WMS 中初始化的);

  final InputMonitor mInputMonitor = new InputMonitor(this);
  public InputMonitor getInputMonitor() {
      return mInputMonitor;
  }

關(guān)于 InputMonitor 是 IMS 和 WMS 之間的紐帶精刷,其具體的交互是通過 InputDispatcher 完成的拗胜,在源碼中具體表現(xiàn)為兩個(gè)方面:InputDispatcher -> InputMonitor -> WMS 和 WMS -> InputMonitor -> InputDispatcher;

// InputDispatcher -> InputMonitor -> WMS:實(shí)現(xiàn)了 IMS 中的 WindowManagerCallbalck 接口
    public interface WindowManagerCallbacks {
        public void notifyConfigurationChanged();  // 配置變更
        public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen); // LID 開關(guān)
        public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
        // 連接 InputDispatcher 與應(yīng)用的 socket 連接怒允;
        public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
        // ANR 
        public long notifyANR(InputApplicationHandle inputApplicationHandle,
                InputWindowHandle inputWindowHandle, String reason);
        // 以下三個(gè)回調(diào)接口是 WMS 處理事件時(shí)優(yōu)先
        public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
        public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
        public long interceptKeyBeforeDispatching(InputWindowHandle focus,
                KeyEvent event, int policyFlags);
        // 事件在整個(gè)處理流程均未處理時(shí)埂软,與 WMS 協(xié)商解決
        public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
                KeyEvent event, int policyFlags);
        public int getPointerLayer();
    }
// 當(dāng)IMS接收到相應(yīng)輸入事件后,會(huì)通過mWindowManagerCallbacks 來調(diào)用mInputMonitor對(duì)象方法:
// return  mWindowManagerCallbacks.notifyConfigurationChanged();
// return mWindowManagerCallbacks.notifyANR();
// ...



// WMS -> InputMonitor -> InputDispatcher:通過 InputMonitor 向 WMS 提供訪問一系列訪問 InputDistacher 的接口纫事;
// 比如 InputDispatcher 中的當(dāng)前窗口焦點(diǎn)勘畔,就是WMS 通過 InputMonitor 中的 updateInputWindowsLw() 通知的;

在這里先預(yù)留兩個(gè)問題:InputDispatcher 時(shí)如何通知應(yīng)用程序窗口有事件產(chǎn)生丽惶,又是如何與目標(biāo)窗口(InputTaget)之間進(jìn)行通信的炫七?

1.2 InputManagerService 的初始化和 start() 方法:IMS 從 java 層到 native 層的啟動(dòng);
(1)InputManagerService.java 的構(gòu)造函數(shù):從 InputManagerService.java 構(gòu)造函數(shù)開始钾唬,初始化 input 輸入事件關(guān)鍵類万哪;

//(1)InputManagerService.java中:構(gòu)造函數(shù)
public InputManagerService(Context context) {
    this.mContext = context;
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
    // native方法,com_android_server_input_InputManagerService.cpp中
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    LocalServices.addService(InputManagerInternal.class, new LocalService());
}



//(2)nativeInit():native方法抡秆,com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    // MessageQueue的指針
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    // native 層的 NativeInputManager 對(duì)象奕巍,并返回該對(duì)象的指針,賦值給 Java 層的 mPtr琅轧;
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    // reinterpret_cast() 用在任意指針(或引用)類型之間的轉(zhuǎn)換
    return reinterpret_cast<jlong>(im);
}



//(3)native 層 NativeInputManager的構(gòu)造函數(shù):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();
    // 這就是 input 輸入事件番外2 中提到的 mInputManager(輸入事件的幾個(gè)關(guān)鍵類都在其中)
    mInputManager = new InputManager(eventHub, this, this);  
}



//(4)native 層 InputManager 的構(gòu)造函數(shù):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();  // 執(zhí)行下方的 initialize() 方法
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

(2)InputManagerService.java 的 start() 方法:
(a)input 輸入事件從 java 層 的啟動(dòng)到 native 方法 nativeStart(mPtr) 的執(zhí)行伍绳;
(b)nativeStart(mPtr) 方法啟動(dòng)處理線程:處理線程啟動(dòng)后,就代表 IMS 在 native 層成功啟動(dòng)乍桂;

//(a)InputManagerService.java 的 start() 方法:從 java 層啟動(dòng) IMS 
public void start() {
    Slog.i(TAG, "Starting input manager");
    // 在 InputManagerService 的對(duì)象 inputManager 初始化過程中執(zhí)行的 nativeInit() 已經(jīng)對(duì) mPtr 賦值冲杀;
    nativeStart(mPtr);  // 關(guān)鍵方法

    // 將inputmanagerservice添加到 Wathcdog中,Watchdog檢測(cè)service是否正常工作
    Watchdog.getInstance().addMonitor(this);
    // 監(jiān)聽Settings.System.POINTER_SPEED睹酌、Settings.System.SHOW_TOUCHES 的變化权谁,并通知native層
    registerPointerSpeedSettingObserver();
    registerShowTouchesSettingObserver();

    // 接收 ACTION_USER_SWITCHED,這是關(guān)于多用戶切換的操作
    mContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updatePointerSpeedFromSettings();
            updateShowTouchesFromSettings();
        }
    }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);

    updatePointerSpeedFromSettings();
    updateShowTouchesFromSettings();
}



//(b)nativeStart(mPtr):com_android_server_input_InputManagerService.cpp 中
static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
    // 將傳入的 mPtr 強(qiáng)轉(zhuǎn)為 NativeInputManager 對(duì)象憋沿;reinterpret_cast() 方法上面有介紹過
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    // 執(zhí)行的是 InputManager 的 start() 方法旺芽;
    // 1.2 的代碼塊 c 中:在初始化 NativeInputManager 時(shí)(構(gòu)造函數(shù)中),也對(duì)成員變量 mInputManager 進(jìn)行了初始化
    status_t result = im->getInputManager()->start();  
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

status_t InputManager::start() {
    // 執(zhí)行 mDispatcherThread 和 mReaderThread 的 run() 方法辐啄,啟動(dòng)處理線程采章;  詳見 2 中的分析;
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
    // 先啟動(dòng)分發(fā)線程壶辜,再啟動(dòng)讀取線程悯舟;這里是為了保證當(dāng)事件開始讀取時(shí),一定會(huì)被分發(fā)砸民;
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);
        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}

至此抵怎,IMS 在 native 層的啟動(dòng)過程基本完成奋救,接下來分析線程啟動(dòng)后,做了哪些事情反惕、事件的讀取與分發(fā)又是如何完成的 (涉及到后續(xù)章節(jié)尝艘,待更新);

2. 處理線程的分析

Thread 類是 Android 為線程操作而做的一個(gè)封裝姿染,其內(nèi)部封裝了 pthread 線程工具背亥,提供了與 Java 層Thread 類相似的 API;Thread 類提供了一個(gè)名為 threadLoop() 的純虛函數(shù)盔粹,當(dāng)線程開始運(yùn)行后隘梨,將會(huì)在內(nèi)建的線程循環(huán)中不斷地調(diào)用 threadLoop(),直到此函數(shù)返回 false舷嗡,則退出線程循環(huán),從而結(jié)束線程嵌莉。

2.1 線程類簡(jiǎn)介:
(1)Thread 類的構(gòu)造函數(shù):

Thread::Thread(bool canCallJava)  //注意這里需要傳入的參數(shù) canCallJava
    :   mCanCallJava(canCallJava),
        mThread(thread_id_t(-1)),
        mLock("Thread::mLock"),
        mStatus(NO_ERROR),
        mExitPending(false), mRunning(false)
#ifdef HAVE_ANDROID_OS
        , mTid(-1)
#endif
{
}

(2)Thread 類的 run() 方法:

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);

    if (mRunning) {
        // thread already started
        return INVALID_OPERATION;
    }

    // reset status and exitPending to their default value, so we can
    // try again after an error happened (either below, or in readyToRun())
    mStatus = NO_ERROR;
    mExitPending = false;
    mThread = thread_id_t(-1);
    
    // hold a strong reference on ourself
    mHoldSelf = this;
    mRunning = true;

    bool res;
   //如果mCanCallJava為真进萄,則調(diào)用createThreadEtc函數(shù),線程函數(shù)是_threadLoop锐峭。
   //_threadLoop是Thread.cpp中定義的一個(gè)函數(shù)中鼠。
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }
    
    if (res == false) {
        mStatus = UNKNOWN_ERROR;   // something happened!
        mRunning = false;
        mThread = thread_id_t(-1);
        mHoldSelf.clear();  // "this" may have gone away after this.

        return UNKNOWN_ERROR;
    }

(3)_threadLoop(void* user) 方法:循環(huán)調(diào)用 threadLoop();

int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);
    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> weak(strong);
    self->mHoldSelf.clear();

    // ...
    bool first = true;

    do {    // 循環(huán)調(diào)用 threadLoop()
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) {
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }

        {
        Mutex::Autolock _l(self->mLock);
        if (result == false || self->mExitPending) {  // threadLoop() 返回為 false 時(shí)沿癞,結(jié)束線程
            self->mExitPending = true;
            self->mRunning = false;
            self->mThread = thread_id_t(-1);
            // note that interested observers blocked in requestExitAndWait are
            // awoken by broadcast, but blocked on mLock until break exits scope
            self->mThreadExitedCondition.broadcast();
            break;
        }
        }
        strong.clear();  // 釋放引用
        // And immediately, re-acquire a strong reference for the next loop
        strong = weak.promote();

    } while(strong != 0);

    return 0;
}

這里對(duì)線程只是做了簡(jiǎn)單的介紹援雇,如果想進(jìn)一步了解,可以參考 framework 線程類椎扬;
2.2 事件讀取線程 InputReaderThread 和分發(fā)線程 InputDispatcherThread:
(1)InputDispatcherThread:
構(gòu)造函數(shù):這里只是構(gòu)建出 InputDispatcherThread 的實(shí)例惫搏;

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

循環(huán)執(zhí)行任務(wù):通過上面可知,nativeStart(mPtr) -> InputManager::start() 中執(zhí)行了分發(fā)線程的 run() 方法蚕涤,而 run() 方法中循環(huán)執(zhí)行 threadLoop() 方法筐赔,接下來看看這個(gè)方法:

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();  // 這里也很簡(jiǎn)單,也就是循環(huán)執(zhí)行 mDispatcher 的 dispatchOnce() 方法揖铜;
    return true;  // return true 表示一直循環(huán)執(zhí)行
}

(2)InputReaderThread:與 InputDispatcherThread 一致茴丰,比較簡(jiǎn)單;

// 構(gòu)造函數(shù)
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}

// 循環(huán)執(zhí)行任務(wù)
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();  // 循環(huán)執(zhí)行 mReader的 loopOnce() 方法天吓;
    return true;
}

小結(jié):

  • SystemServer.java 中 startOtherServices():創(chuàng)建 java 層的 IMS(inputManager)贿肩,同時(shí)創(chuàng)建 WMS(wm),并通過inputManager.setWindowManagerCallbacks(wm.getInputMonitor()) 進(jìn)行相互關(guān)聯(lián)龄寞;最后執(zhí)行 inputManager.start() 啟動(dòng) IMS汰规;
    (1)InputManagerService.java 中的構(gòu)造函數(shù):調(diào)用 mPtr = nativeInit(...),創(chuàng)建 native 層的 NativeInputManager萄焦,并保存其返回的指針于 mPtr 中控轿;
    (2)InputManagerService.java 的 start() 方法:調(diào)用 nativeStart(mPtr)冤竹,IMS 在 native 層啟動(dòng);

  • nativeInit() 方法:創(chuàng)建 NativeInputManager 對(duì)象并返回其指針茬射,同時(shí)創(chuàng)建 InputManager 的對(duì)象和輸入事件所需幾個(gè)關(guān)鍵類的對(duì)象(EventHub, InputDispatcher, InputDispatcherThread, InputReader, InputReaderThread) 鹦蠕;

  • nativeStart(mPtr) 方法:執(zhí)行了事件分發(fā)線程(mDispatcherThread) 和 事件讀取線程(mReaderThread)的 run() 方法,至此 native 層 IMS 的啟動(dòng)完成在抛;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钟病,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子刚梭,更是在濱河造成了極大的恐慌肠阱,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朴读,死亡現(xiàn)場(chǎng)離奇詭異屹徘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)衅金,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門噪伊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人氮唯,你說我怎么就攤上這事鉴吹。” “怎么了惩琉?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵豆励,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瞒渠,道長(zhǎng)良蒸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任在孝,我火速辦了婚禮诚啃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘私沮。我一直安慰自己始赎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布仔燕。 她就那樣靜靜地躺著造垛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晰搀。 梳的紋絲不亂的頭發(fā)上浸赫,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天糠睡,我揣著相機(jī)與錄音增蹭,去河邊找鬼。 笑死乡翅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罪郊。 我是一名探鬼主播蠕蚜,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼悔橄!你這毒婦竟也來了靶累?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤癣疟,失蹤者是張志新(化名)和其女友劉穎挣柬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睛挚,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邪蛔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竞川。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片店溢。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖委乌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荣回,我是刑警寧澤遭贸,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站心软,受9級(jí)特大地震影響壕吹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜删铃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一耳贬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猎唁,春花似錦咒劲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逐纬,卻和暖如春蛔屹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背豁生。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工兔毒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漫贞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓育叁,卻偏偏與公主長(zhǎng)得像迅脐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子擂红,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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