Android Input

http://www.lxweimin.com/p/2bff4ecd86c9
本篇博客主要是過一下Android Input的框架,在熟悉Android input框架后,以及與App的事件傳遞流程后,再過一下事件的處理流程,以及ANR是怎樣發(fā)生的。如果在不了解input的框架情況下就去直接按鍵等事件就會(huì)覺得很卡。

一、Android Input的工作模型

1.1InputDispatcher

InputDispatcher單獨(dú)run在InputDispatcher線程中

1.1.1 InputDispatcher的模型

InputDispatcher的實(shí)現(xiàn)模型是Looper的機(jī)制,其底層根本還是屬于epoll機(jī)制. 只不過Input并沒有使用Looper相關(guān)的Message相關(guān)的功能,也就是說沒有MessageQueue了,僅是單純的使用Looper的addFd功能,以及它的epoll阻塞喚醒功能。

InputDispatcher單獨(dú)運(yùn)行在一個(gè)線程當(dāng)中,當(dāng)線程啟動(dòng)時(shí),它會(huì)不停的調(diào)用threadLoop,

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

每一次threadLoop都會(huì)調(diào)用InputDispatcher的dispatchOnce函數(shù)

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

上面dispatchOnce先會(huì)嘗試去獲得pending的Commands,然后處理這些pending的命令。當(dāng)這些都處理完成后,會(huì)調(diào)用Looper的pollOnce,傳進(jìn)去的參數(shù)是timeout. 正常情況下,如果當(dāng)前沒有喚醒,或沒有fd的回調(diào)(這個(gè)后面會(huì)講,也就是App消費(fèi)了input事件的回調(diào)), 那么InputDispatcher線程就一直block在Looper的epoll那里,直到被喚醒。具體可以參考Android Handler/Looper

1.1.2 Looper的喚醒

1.1.1已經(jīng)大致的說了下InputDispatcher線程的工作模型,沒有事件時(shí)它會(huì)block在Looper的epoll處. 那它啥時(shí)候被喚醒呢?其實(shí)很簡(jiǎn)單,查找哪些地方調(diào)用了 mLooper->wake();,還有一個(gè)地方是App消費(fèi)了input事件的回調(diào)(后面講)

notifyConfigurationChanged()
notifyKey()
notifyMotion()
notifySwitch();
injectInputEvent()
setInputWindows()
setFocusedApplication()
setInputDispatchMode()
setInputFilterEnabled()
transferTouchFocus()
registerInputChannel()
unregisterInputChannel()
monitor()

上面這些函數(shù)都有調(diào)用到mLooper->wake的可能。
如notifyKey()/notifyMotion() 等, "間接"來自InputReader線程的通知。
setInputWindows()/setFocusedApplication()等,"間接"來自android.display線程的通知。

如果 App 消費(fèi)了Input事件, Looper也會(huì)被喚醒,接著handleReceiveCallback被回調(diào)。

這里以notifyConfigurationChanged為例

void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
    bool needWake;
    { // acquire lock  防止多線程同時(shí)訪問,這里加了一個(gè)mLock的互斥鎖。
        AutoMutex _l(mLock);
        ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
        needWake = enqueueInboundEventLocked(newEntry);
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

notifyConfigurationChanged其實(shí)挺簡(jiǎn)單的,生成一個(gè)ConfigurationChangedEntry,然后通過enqueueInboundEventLocked函數(shù)加入到mInboundQueue隊(duì)列中。具體的可以看參考下enqueueInboudnEventLocked. 然后根據(jù)needWake決定是否喚醒Looper, 這個(gè)needWake,默認(rèn)在mInboundQueue里沒有數(shù)據(jù)時(shí)為true, 當(dāng)mInboundQueue里有數(shù)據(jù)時(shí),此時(shí)Looper應(yīng)該已經(jīng)被喚醒了,且正在處理mInboundQueue里的命令,此時(shí)已經(jīng)是wake的狀態(tài),所以沒有必要再次wake一次。

    Queue<EventEntry> mInboundQueue; 

mInboundQueue申明為一個(gè)隊(duì)列,主要是保存InputReader中傳過來的EventEntry.

EventEntry主要有如下的幾種類型


EventEntry

mPendingEvent是EventEntry類型,它根據(jù)type類型轉(zhuǎn)為具體的EventEntry,如 MotionEntry, KeyEntry等等。

1.1.3 InputDispatcher處理Commands

1.1的dispatchOnce()代碼中可以看出,InputDispatcher在當(dāng)前沒有commands時(shí)會(huì)直接調(diào)用dispatchOnceInnerLocked一次,而dispatchOnceInnerLocked的目的就是去獲得Commands.

如果當(dāng)前有Commands了(比如1.2已經(jīng)ConfigurationEventEntry),就不會(huì)去獲得Commands,而是直接run已經(jīng)有的Commands.

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            if (!mPendingEvent) {
                return;
            }
        } else {
            mPendingEvent = mInboundQueue.dequeueAtHead();
        }
        ...
    } //這個(gè)if塊意在獲得一個(gè)mPendingEvent,如果沒有Pending的event, 直接返回掉

   //如果已經(jīng)走到下面,mPendingEvent是不為空的,也就是有待處理的事件
    switch (mPendingEvent->type) {
    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
        done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
        break;
    }
    case EventEntry::TYPE_DEVICE_RESET: {
    case EventEntry::TYPE_KEY: {
    case EventEntry::TYPE_MOTION: {
}

這里依然以mPendingEntry為ConfigurationChangedEntry為例,

bool InputDispatcher::dispatchConfigurationChangedLocked(
        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyConfigurationChangedInterruptible);
    commandEntry->eventTime = entry->eventTime;
    return true;
}
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}

生成一個(gè)Commands,它的函數(shù)指針指向doNotifyConfigurationChangedInterruptible.

dispatchOnce在runCommandsLockedInterruptible()里去處理所有的Commands,這時(shí)會(huì)調(diào)用doNotifyConfigurationChangedInterruptible,

void InputDispatcher::doNotifyConfigurationChangedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
    mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
    mLock.lock();
}

最終調(diào)用mPolicy, 也就是NativeInputManager中的notifyConfigurationChanged, 將結(jié)果返回到j(luò)ava層去。

這種情況并沒有包含 Key/Motion這樣的事件情況,(后續(xù)會(huì)繼續(xù)介紹)

1.1.4 小結(jié)

  • a). InputDispatcher使用Looper的epoll模型, 意味著在沒有命令處理時(shí)會(huì)block在epoll處
  • b). 當(dāng)IMS(java層) 或 InputReader有事件要dispatch時(shí),它們會(huì)喚醒InputDispatcher
  • c). InputDispatcher被喚醒后,會(huì)從mInboundQueue隊(duì)列中查找pending的event, 然后生成對(duì)應(yīng)的Commands, 最后執(zhí)行這些Commands.
flow

1.2 InputReader

InputReader單獨(dú)運(yùn)行在InputReaderThread中,它依然繼承于Thread類,也就是當(dāng)InputReaderThread線程運(yùn)行起來后它會(huì)一直調(diào)用threadLoop()函數(shù)。InputReader并沒有使用Looper機(jī)制,不過它使用到了EventHub里的 epoll 機(jī)制,和Looper的epoll機(jī)制一樣。

1.2.1 EventHub

EventHub在NativeInputManager里初始化,并沒有放到InputReader里初始化,其實(shí)完全可以放到InputReader里初始化的呢? why???

EventHub::EventHub(void) : ... {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //創(chuàng)建一個(gè)epoll文件字柄
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    mINotifyFd = inotify_init(); //創(chuàng)建一個(gè)inotify fd
    //inotify 監(jiān)聽 /dev/input 目錄
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
   ...
    //將inotify fd加入到epoll中
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    int wakeFds[2];
    //創(chuàng)建一個(gè)管道
    result = pipe(wakeFds);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
    ...
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}

從EventHub的初始化可以看出,它通過inotify監(jiān)聽 /dev/input里的文件的變化,另外創(chuàng)建了一個(gè)管道, 將read fd加入到epoll中去監(jiān)聽,而write fd主要用來喚醒epoll.

從 EventHub::wake()函數(shù)可以看出來

void EventHub::wake() {
    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);
}

通過調(diào)用wake函數(shù),往 write fd中寫入一個(gè)字節(jié),然后epoll監(jiān)聽的read fd就有事件發(fā)生,epoll就被喚醒了,這和Looper的wake機(jī)制一模一樣。


EventHub機(jī)制

1.2.2 InputReader初始化

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) {
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

InputReader的構(gòu)造函數(shù)中初始化了一個(gè)QueuedInputListener, 它接收InputListenerInterface作為它的參數(shù),從InputReader調(diào)用可知,這個(gè)InputListenerInterface其實(shí)就是InputDispatcher, QueueInputListener只是作為InputDispatcher的Wrapper.

1.2.2.1 讀取Java層中的配置

void InputReader::refreshConfigurationLocked(uint32_t changes) {
    mPolicy->getReaderConfiguration(&mConfig);
    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
    ...
}

refreshConfigurationLocked的主要是通過getReaderConfiguration調(diào)用到Java層的配置信息,保存到mConfig里。具體調(diào)用到如下的接口

getVirtualKeyQuietTimeMillis
getExcludedDeviceNames
getHoverTapTimeout
getDoubleTapTimeout
getLongPressTimeout
getHoverTapSlop

這些函數(shù)具體實(shí)現(xiàn)就是去取一些framework-res.jar里的一些配置信息。讀到的信息最后都會(huì)設(shè)置到InputReader里不同的模塊中,比如"ecluded device name"會(huì)去設(shè)置 EventHub 里mExcludedDevices等等。

1.2.3 InputReader運(yùn)作起來

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

InputReaderThread線程開啟后會(huì)不停的運(yùn)行threadLoop函數(shù), 而它會(huì)調(diào)用InputReader的loopOnce函數(shù)

void InputReader::loopOnce() {
    ... 太多細(xì)節(jié)就不多說了
    //獲得事件, 沒有事件就block在EventHub中的epoll處
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        if (count) {  //有事件了,著手處理事件
            processEventsLocked(mEventBuffer, count);
        }
        ...
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    //將獲得的事件傳給InputDispatcher去做處理
    mQueuedListener->flush();
}

EventHub的getEvents函數(shù)太長(zhǎng),這里就不貼出來了, 它主要就是獲得事件,這里的事件,并不單單指input事件,它還包括輸入設(shè)備的add/remove等相關(guān)的事件.

獲得輸入設(shè)備加入和刪除事件

輸入設(shè)備的加入和移除事件跟幾個(gè)變量非常相關(guān).

  • mNeedToReopenDevices
    表示需要重新打開輸入設(shè)備, 它會(huì)先close當(dāng)前已經(jīng)打開的設(shè)置做一些清理工作,具體參見closeAllDevicesLocked() 函數(shù),將沒有delete掉的設(shè)備用mClosingDevices來表示,最后會(huì)把 mNeedToScanDevices 置為true.
  • mClosingDevices
    表示當(dāng)前沒有被delete掉的設(shè)備,這getEvents里就將這些設(shè)備依次刪除掉, 并生成 DEVICE_REMOVED事件
  • mNeedToScanDevices
    該變量表示需要掃描輸入設(shè)備,并打開輸入設(shè)備,加入到mDevices中,用mOpeningDevices表示這些設(shè)備的Head
  • mOpeningDevices
    表示剛剛打開的所有的設(shè)備,它是一個(gè)單鏈表的HEAD, getEvents會(huì)將它所保存的所有剛打開的設(shè)備創(chuàng)建一個(gè)DEVICE_ADDED事件
  • mNeedToSendFinishedDeviceScan
    表示finish 掃描輸入設(shè)備, 會(huì)生成一個(gè)FINISHED_DEVICE_SCAN事件

getEvents通過將產(chǎn)生的事件放到mEventBuffer所指向的一維數(shù)給中,然后通過最后一個(gè)事件的地址-mEventBuffer地址就可以得到當(dāng)前有多少事件,很巧妙。

獲得輸入設(shè)備的事件
輸入設(shè)備加入后,如果沒有具體的事件產(chǎn)生的話,它就會(huì)進(jìn)入epoll的阻塞狀態(tài)。

    for (;;) {
        //處理變化的事件
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            //針對(duì)EventHub::wake
            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    awoken = true;
                    ...
                }
            }
            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                ...
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i]; //獲得具體的輸入事件
                        //將輸入事件保存到mEventBuf中
                       event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1; //指向下一個(gè)事件
                        capacity -= 1;
        }
        //當(dāng)有事件后就直接退出, awoken表示通過調(diào)用EventHub::wake函數(shù)喚醒epoll,也直接退出
        if (event != buffer || awoken) {
            break;
        }

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        if (pollResult < 0) {
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }

當(dāng)有事件產(chǎn)生后,epoll_wait就會(huì)返回,將有改變的個(gè)數(shù)放到mPendingEventCount中, 再下一輪的for循環(huán)中, 就在while循環(huán)中處理變化的事件.

while循環(huán)其實(shí)挺簡(jiǎn)單,主要是通過從改變的輸入設(shè)備中讀取輸入事件,然后保存到mEventBuf中,然后從getEvents返回。

處理事件

void InputReader::loopOnce() {  
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
        if (count) {    
            processEventsLocked(mEventBuffer, count); // 處理事件
        } 
    mQueuedListener->flush();
}

InputReader在有事件發(fā)生后,getEvents就會(huì)返回,如果返回的count > 0時(shí),就著手處理這些事件

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            //處理輸入事件
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                //輸入設(shè)備加入
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::DEVICE_REMOVED:
                //輸入設(shè)備移出
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                //configuration 改變
                handleConfigurationChangedLocked(rawEvent->when);
                break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

processEventsLocked根據(jù)返回的那些事件依次處理,包括對(duì)輸入設(shè)備的增加和移出,以及輸入事件的處理。

這里依然以handleConfigurationChangedLocked為例

void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
    NotifyConfigurationChangedArgs args(when);
    mQueuedListener->notifyConfigurationChanged(&args);
}

Vector<NotifyArgs*> mArgsQueue;
void QueuedInputListener::notifyConfigurationChanged(
        const NotifyConfigurationChangedArgs* args) {
    mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
}

handleConfigurationChangedLocked生成一個(gè)NotifyConfigurationChangedArgs然后通過QueuedListener,將NotifyConfigurationChangedArgs加入到mArgsQueue這個(gè)vector中

當(dāng)InputReader::loopOnce()在處理完事件后會(huì)調(diào)用 mQueuedListener->flush();

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyConfigurationChanged(this);
}

flush函數(shù)對(duì)mArgsQueue里所有的NotifyArgs,調(diào)用notify, 這里mInnerListener也就是InputDispatcher, 如NotifyConfigurationChangedArgs為例,調(diào)用InputDispatcher的notifyConfigurationChanged將事件傳入到了InputDispatcher中了。

至此InputReader的工作模型就介紹完了。

1.3 Input java層與jni層相互調(diào)用

已經(jīng)知道Input工作在三個(gè)線程中,一個(gè)java線程,兩個(gè)jni線程(InputReader, InputDispatcher)

  • Java通過jni獲得相關(guān)信息

Java層通過各種 nativeXXX去獲得jni中的相關(guān)信息,具體可以查詢InputManagerService.java中的nativeXXX開頭的函數(shù), 如

nativeGetKeyCodeState()
nativeSetFocusedApplication()

它們對(duì)應(yīng)的jni實(shí)現(xiàn)如下

static jint nativeGetSwitchState(JNIEnv* /* env */, jclass /* clazz */,
        jlong ptr, jint deviceId, jint sourceMask, jint sw) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    return (jint) im->getInputManager()->getReader()->getSwitchState(
            deviceId, uint32_t(sourceMask), sw);
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject applicationHandleObj) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    im->setFocusedApplication(env, applicationHandleObj);
}
void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) {
    sp<InputApplicationHandle> applicationHandle =
            android_server_InputApplicationHandle_getHandle(env, applicationHandleObj);
    mInputManager->getDispatcher()->setFocusedApplication(applicationHandle);
}

如圖,它們都是通過NativeInputManager去獲得InputDispatcher或InputReader去做相應(yīng)的處理,注意,這些都是在java線程中調(diào)用的,為了線程安全,在相應(yīng)的實(shí)現(xiàn)中都有鎖。

  • Jni回調(diào)java接口

同樣InputDispatcher和InputReader線程都有可能調(diào)用到j(luò)ava層的接口,具體就不多說了。

二、Android Input與App

第一節(jié)已經(jīng)基本說了下Android Input的代碼結(jié)構(gòu),input的作用就是獲得輸入設(shè)備產(chǎn)生的事件,并且分發(fā)出來,那分發(fā)到哪里去了呢? 當(dāng)然是分發(fā)到了Focused的App里了。

                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel(); //生成一個(gè)InputChannel
                }
                ...
                try {
                    ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

App在addView時(shí),會(huì)在ViewRootImpl中生成InputChannel, InputChannel實(shí)現(xiàn)了Parcelable, 所以它可以通過Binder傳輸。具體是通過addDisplay()將當(dāng)前window加入到WMS中管理,同時(shí)也會(huì)有相應(yīng)的input的處理.

2.1 SystemServer端中WMS的addWindow

public int addWindow(Session session, IWindow client, ... InputChannel outInputChannel) {
            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
}

addWindow會(huì)通過WindowState去openInputChannel()

    void openInputChannel(InputChannel outInputChannel) {
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
        }
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }

openInputChannel做了三件事,
一是通過openInputChannelPair也就是nativeOpenInputChannelPair去打開一組socket用于跨進(jìn)程通信. 具體可以參考

android_view_InputChannel_nativeOpenInputChannelPair ->  InputChannel::openInputChannelPair()
  • 創(chuàng)建一對(duì)socket pair

  • transfer給outInputChannel

        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
  • 注冊(cè)InputChannel和InputWindowHandle給Input
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);


在registerInputChannel后,InputDispatcher就開始監(jiān)聽App在Server端的InputChannel了。

2.2 App端獲得InputChannel

SystemServer端生成的InputChannel是在SystemServer進(jìn)程中,App進(jìn)程不能直接訪問其地址,那App是怎么獲得InputChannel的呢? 當(dāng)然是通過Binder了.
App的ViewRootImpl在調(diào)用addToDisplay返回后,ViewRootImpl里的InputChannel就指向了正確的InputChannel, 它是Client端,即Client端的fd與SystemServer進(jìn)程中Server端的fd組成 socket pair, 它們就可以雙向通信了。 那App端的InputChannel是如何正確的Client的InputChannel呢?

在 IWindowSession類中

            public int addToDisplay(... android.view.InputChannel outInputChannel)  {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                    ...
                    mRemote.transact(Stub.TRANSACTION_addToDisplay, _data, _reply, 0);
                    ...
                    if ((0 != _reply.readInt())) {
                        outInputChannel.readFromParcel(_reply);
                    }
            }

Binder Proxy端(App)端中 ViewRootImpl中的InputChannel是從Parcel里讀出來的。

        public boolean onTransact(...) {
                case TRANSACTION_addToDisplay: {
                    ...
                    android.view.InputChannel _arg8;
                    _arg8 = new android.view.InputChannel();
                    int _result = this.addToDisplay(...);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    ...
                    if ((_arg8 != null)) {
                        reply.writeInt(1);
                        _arg8.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }

Binder Server端(SystemServer)在 onTransact里生成一個(gè)局部的InputChannel,在addDisplay處理完后,就將InputChannel序列化到Parcel中傳遞到App端.

static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
        jobject parcelObj) {
    Parcel* parcel = parcelForJavaObject(env, parcelObj);
    if (parcel) {
        NativeInputChannel* nativeInputChannel =
                android_view_InputChannel_getNativeInputChannel(env, obj);
        if (nativeInputChannel) {
            sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();

            parcel->writeInt32(1);
            parcel->writeString8(inputChannel->getName());
            parcel->writeDupFileDescriptor(inputChannel->getFd());
        } else {
            parcel->writeInt32(0);
        }   
    }   
}

序列化的過程其實(shí)就三個(gè),寫name, 然后writeDupFileDescriptor, dup文件句柄。

反序列化過程


static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
        jobject parcelObj) {
    if (android_view_InputChannel_getNativeInputChannel(env, obj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "This object already has a native input channel.");
        return;
    }

    Parcel* parcel = parcelForJavaObject(env, parcelObj);
    if (parcel) {
        bool isInitialized = parcel->readInt32();
        if (isInitialized) {
            String8 name = parcel->readString8();
            int rawFd = parcel->readFileDescriptor();
            int dupFd = dup(rawFd);
            if (dupFd < 0) {
                return;
            }

            InputChannel* inputChannel = new InputChannel(name, dupFd);
            NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
            android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
        }
    }
}

反序化就是在 App端 native層生成一個(gè)InputChannel,然后dup 文件句柄,設(shè)置等等


2.3 App端注冊(cè)InputChannel到Looper

通過 2.2 小節(jié),App端已經(jīng)獲得了InputChannel,以及正確的socket fd. 那要怎么利用起來呢?

ViewRootImpl在addDisplay后,會(huì)生成一個(gè)WindowInputEventReceiver

                if (mInputChannel != null) {
                    ...
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

Looper.myLooper()是App進(jìn)程Main線程的Looper.

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }

WindowInputEventReceiver會(huì)調(diào)用父類InputEventReceiver構(gòu)造函數(shù),然后通過nativeInit函數(shù)將InputChannel的fd加入到Looper的epoll中去。

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

也就是說在 App進(jìn)程的Main線程的Looper中監(jiān)聽I(yíng)nputChannel的Client端。當(dāng)有事件發(fā)生時(shí),Looper就會(huì)回調(diào) NativeInputEventReceiver::handleEvent()


2.4 小節(jié)

App通過Binder獲得InputChannel的client端,然后將fd加入到App進(jìn)程的Main線程中監(jiān)聽。

三、input事件的傳遞流程

3.1 Input事件傳遞Overview

在了解了input框架和App端與Input的關(guān)系后,input的按鍵等相關(guān)事件的傳遞過程就相當(dāng)簡(jiǎn)單了。

Keyevent.png

圖中綠色方塊表示調(diào)用java的方法.

InputReader在將事件加入到mInboundQueue之前會(huì)嘗試interceptKey, 如果按鍵被截獲成功,那么在InputDispatcher的紅色塊會(huì)被drop掉
以及filterInputEvent. 如果filter成功,那在InputReader線程中就直接返回,不會(huì)再將Event傳遞到InputDispatcher中.

另外



一個(gè)InputDevice可支持多種Mapper, 取決于mClasses的值, 具體是在

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
    ...
    // Vibrator-like devices.
    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
        device->addMapper(new VibratorInputMapper(device));
    }    

    // Keyboard-like devices.
    uint32_t keyboardSource = 0; 
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }    
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }    
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }    
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }
    ...

3.2 Native中Focused的App與Window

InputApplicationHandle表示的是一個(gè)Focused的Application
InputWindowHandle表示是當(dāng)前系統(tǒng)中所有的Window, 當(dāng)然這里是指可以接收Input事件的窗口, 它可以是多個(gè),只不過只一個(gè)當(dāng)前獲得焦點(diǎn)的窗口。

3.1 設(shè)置Focused 的 InputApplicationHandle

當(dāng)一個(gè)App resumed后,AMS就會(huì)調(diào)用setResumedActivityUncheckLocked去更新AMS的一些狀態(tài), 接著通知WMS去setFocusedApp


3.2 設(shè)置Focused 的 InputWindowHandle

void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
    Vector<sp<InputWindowHandle> > windowHandles; //保存所有從JAVA層傳入的InputWindowHandle

    if (windowHandleObjArray) {
        jsize length = env->GetArrayLength(windowHandleObjArray);
        for (jsize i = 0; i < length; i++) {
            jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
            //獲得一個(gè)InputWindowHandle
            sp<InputWindowHandle> windowHandle =
                    android_server_InputWindowHandle_getHandle(env, windowHandleObj);
            if (windowHandle != NULL) {
                //保存到windowHandles里
                windowHandles.push(windowHandle);
            }    
        }    
    }    

    mInputManager->getDispatcher()->setInputWindows(windowHandles);
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
    { // acquire lock
        AutoMutex _l(mLock);
        //獲得舊的所有的window的InputWindowHandle
        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
        mWindowHandles = inputWindowHandles; //保存所有InputWindowHandle

        sp<InputWindowHandle> newFocusedWindowHandle;
        bool foundHoveredWindow = false;
        for (size_t i = 0; i < mWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
            //updateInfo這里是去獲得java層對(duì)應(yīng)的InputWindowHandle的值, 并保存到InputWindowInfo里
            //只有當(dāng)InputWindowHandle里有InputChannel時(shí),這個(gè)Window才可能接收 input事件
            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                mWindowHandles.removeAt(i--);
                continue;
            }
            //只有當(dāng)InputWindowHandle hasFocus值為true時(shí),此時(shí)將會(huì)改變focused InputWindowHandle
            if (windowHandle->getInfo()->hasFocus) {
                newFocusedWindowHandle = windowHandle;
            }    
        }    

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            if (mFocusedWindowHandle != NULL) {
               // Focused InputWindowHandle改變了,此時(shí)會(huì)cancel掉上一個(gè)Focused的Window的事件
                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
                if (focusedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                            "focus left window");
                    synthesizeCancelationEventsForInputChannelLocked(
                            focusedInputChannel, options);
                }    
            }    
            //指向最新的Focused InputWindowHandle
            mFocusedWindowHandle = newFocusedWindowHandle;
        }
        //release沒在mWindowsHandle里的舊的InputWindowHandle的信息
        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
            if (!hasWindowHandleLocked(oldWindowHandle)) {
                oldWindowHandle->releaseInfo();
            }
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}

從代碼中可以看出,將java層中所有的InputWindowHandle都會(huì)加入到InputDispatcher里來保存,然后遍歷所有的InputWindowHandle,根據(jù)其是否獲得了焦點(diǎn)來將它設(shè)置為mFocusedWindowHandle

特別注意的是,InputWindowHandle里的InputWindowInfo的值都是通過獲得Java層對(duì)應(yīng)的InputWindowHandle的值。具體可以參見 NativeInputWindowHandle::updateInfo()

3.3 找到Focused的App與Window

由3.1, 3.2小節(jié)的知識(shí),找到Focused的App與Window就非常簡(jiǎn)單了,為什么需要找到這兩個(gè)呢?因?yàn)楫?dāng)前有輸入事件,輸入事件需要傳遞給當(dāng)前獲得焦點(diǎn)的App的窗口.

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
        const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
    int32_t injectionResult;
    String8 reason;

    //當(dāng)前Focused的App是否正在add window, 意思是還沒有Focused的window
    if (mFocusedWindowHandle == NULL) {
        if (mFocusedApplicationHandle != NULL) {
            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                    mFocusedApplicationHandle, NULL, nextWakeupTime,
                    "Waiting because no window has focus but there is a "
                    "focused application that may eventually add a window "
                    "when it finishes starting up.");

            goto Unresponsive;
        }
        //當(dāng)前也沒有Focused的 App
        injectionResult = INPUT_EVENT_INJECTION_FAILED;
        goto Failed;
    }

    // Check permissions.  //檢查是否具體INJECT_EVENT的權(quán)限
    if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
        goto Failed;
    }

    // Check whether the window is ready for more input.
   //進(jìn)一步檢查是否需要 drop
    reason = checkWindowReadyForMoreInputLocked(currentTime,
            mFocusedWindowHandle, entry, "focused");
    if (!reason.isEmpty()) { //如果 reason不為空,就drop掉
        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
        goto Unresponsive;
    }

    // Success!  Output targets.
    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
    //找到正確的InputChannel,加入到InputTargets中
    addWindowTargetLocked(mFocusedWindowHandle,
            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),inputTargets);

    // Done.
Failed:
Unresponsive:
    ...
    return injectionResult;
}

經(jīng)過findFocusedWindowTargetsLocked后就找到了正確的InputChannel, 然后通過socket通信就將事件傳輸?shù)紸pp端了。

3.4 ANR是怎么發(fā)生的?

ANR

可以看出ANR發(fā)生的源頭是 handleTargetsNotReadyLocked. 從字面上來看應(yīng)該是InputTargets還沒有Ready,它主要是在3.3中的findFocusedWindowTargetsLocked中調(diào)用

情況一:mFocusedApplicationHandle != null, mFocusedWindowHandle== null

情況二:mFocusedApplicationHandle 與 mFocusedWindowHandle都不空的情況

  • 當(dāng)前Focused的window PAUSED了
  • Focused的window的 Connection都沒有,也就是還沒有注冊(cè)
  • Focused的window的 Connection 不正常
  • Focused的window的 Connection里塞滿了輸入事件, 還在等著App去finish掉事件
  • 針對(duì) KeyEvent情況,必須上一個(gè)事件完成了才行
  • 針對(duì)Touch事件的情況

3.4.1 正常事件的流程

以 Key事件為例, Key事件按下是ACTION_DOWN, 抬起是ACTION_UP, 現(xiàn)在來看下這兩個(gè)事件的正常流程如下.

  1. 先將事件加入到outBoundQueue,然后publishKeyEvent到App Main線程


    image.png
  2. 然后立馬將A事件從outboundQueue中剝離,加入到waitQueue中


    image.png
  3. App線程處理完按鍵事件了
    App線程會(huì)調(diào)用nativeFinishInputEvent,進(jìn)一步調(diào)用 sendFinishedSignal 向 SystemServer發(fā)送哪個(gè)按鍵事件已經(jīng)被finish, 最后從waitQueue中移出掉事件


    image.png

3.4.2 ANR發(fā)生

1. 假設(shè) App 在處理 A事件(ACTION_DOWN), 沒有返回。

image.png

2. 這時(shí)來了一個(gè)B事件(ACTION_UP).
這時(shí)findFocusedWindowTargetsLocked在checkWindowReadyForMoreInputLocked時(shí)發(fā)現(xiàn)waitQueue里不為空,這時(shí)就要調(diào)用handleTargetsNotReadyLocked了

    reason = checkWindowReadyForMoreInputLocked(currentTime,
            mFocusedWindowHandle, entry, "focused");
    if (!reason.isEmpty()) {
        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
        goto Unresponsive;
    }
int32_t InputDispatcher::handleTargetsNotReadyLocked(...) {
    if (applicationHandle == NULL && windowHandle == NULL) {
       //一般不會(huì)進(jìn)入該分支,這個(gè)情況一般是系統(tǒng)剛啟動(dòng)或systemserver重啟的情況
    } else {
       // mInputTargetWaitCause 默認(rèn)情況下是INPUT_TARGET_WAIT_CAUSE_NONE
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            nsecs_t timeout;
           //獲得超時(shí)時(shí)間,默認(rèn)是5s
            if (windowHandle != NULL) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(
                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
            }
           //設(shè)置cause為 APPLICATION_NOT_READY狀態(tài)
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            //超時(shí)時(shí)間是dispatch這個(gè)事件的時(shí)間+5
            mInputTargetWaitTimeoutTime = currentTime + timeout;
            mInputTargetWaitTimeoutExpired = false;

            //重新設(shè)置正確的mInputTargetWaitApplicationHandle
            mInputTargetWaitApplicationHandle.clear();

            if (windowHandle != NULL) {
                mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
            }
            if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
                mInputTargetWaitApplicationHandle = applicationHandle;
            }
        }
    }
    // 并不會(huì)進(jìn)入,這里顯示mInputTargetWaitTimeoutTime = current + 5s
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        ...
    } else {
        return INPUT_EVENT_INJECTION_PENDING;
    }
}

這時(shí)候B事件就會(huì)設(shè)置正確的mInputTargetWaitCause, mInputTargetWaitApplicationHandle, mInputTargetWaitTimeoutTime.

如果隔了5s后,App Main線程還是沒有返回,這時(shí)再來了一個(gè)C 事件,此時(shí),在handleTargetsNotReadyLocked里就要發(fā)生ANR了

int32_t InputDispatcher::handleTargetsNotReadyLocked(...) {
    if (applicationHandle == NULL && windowHandle == NULL) {
       //一般不會(huì)進(jìn)入該分支,這個(gè)情況一般是系統(tǒng)剛啟動(dòng)或systemserver重啟的情況
    } else {
       // 此時(shí)的mInputTargetWaitCause 是INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
      //不會(huì)進(jìn)入該分支
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
          ...
        }
    }
    // 由于是隔了5s 左右,此時(shí)currentTime 就 大于 mInputTargetWaitTimeoutTime, 這時(shí)就要產(chǎn)生ANR了
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        ...
    } else {
        return INPUT_EVENT_INJECTION_PENDING;
    }
}

3. 假設(shè)A事件(ACTION_DOWN)在5s內(nèi)被consumed了, 那2中就不會(huì)發(fā)生ANR,那2中的B事件是何時(shí)在dispatch出去的呢?

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
    ...
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    Vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

如果findFocusedWindowTargetsLocked返回為INPUT_EVENT_INJECTION_PENDING, 那dispatchKeyLocked就直接返回false, 在本例中,此處返回 false.
所以dispatchOnceInnerLocked并不會(huì)處理 if (done), 這就意思著不會(huì)調(diào)用releasePendingEventLocked,進(jìn)而mPendingEvent也不會(huì)被置為NULL, 這樣下一輪dispatchOnceInnerLocked中就會(huì)發(fā)現(xiàn)mPendingEvent不為NULL, 就繼續(xù)dispatch上一次沒有被dispatch出去的Event, 好巧妙。

四、小結(jié)

  1. Input工作于三個(gè)線程, android.display, InputReader, InputDispatcher線程
  2. Input與App的通信是通過socket.
  3. InputReader使用EventHub里的epoll機(jī)制, InputDispatcher使用Looper中的epoll機(jī)制
  4. App在consume掉input事件后,會(huì)通過nativeFinishInputEvent去通知Input移出到waitQueue里等待的事件,防止ANR.
  5. ANR的發(fā)生需要三個(gè)事件,第一個(gè)事件,讓App線程處理,且 App線程不返回, 第二個(gè)事件開始計(jì)時(shí),默認(rèn)5s, 第三個(gè)事件在5s結(jié)束后來到,此時(shí)產(chǎn)生ANR
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370