Android Input(八)- ANR原理分析

原創(chuàng)內(nèi)容,轉(zhuǎn)載請(qǐng)注明出處,多謝配合。

先針對(duì)前面的Input調(diào)用流程進(jìn)行一個(gè)簡(jiǎn)單總結(jié):

EventHub: 收集底層硬件設(shè)備tp報(bào)點(diǎn)。打開(kāi)"/dev/input/"目錄下的input設(shè)備,并將其注冊(cè)到epoll的監(jiān)控隊(duì)列中。一旦對(duì)應(yīng)設(shè)備上有可讀的input事件,馬上包裝成event,上報(bào)給InputReader。

InputReader: 獲取EventHub上報(bào)的input事件,通過(guò)deviceid,找到具體的InputDevice,然后匹配上對(duì)應(yīng)的InputMapper來(lái)處理對(duì)應(yīng)input事件。InputMapper最終會(huì)按對(duì)應(yīng)的NotifyArgs數(shù)據(jù)結(jié)構(gòu)對(duì)事件進(jìn)行封裝,并加入到ArgsQueue中。最終flush操作會(huì)將ArgsQueue中的每一個(gè)Arg取出來(lái)轉(zhuǎn)成對(duì)應(yīng)的Entry加入到InputDispatcher的mInboundQueue。

InputDispatcher: InboundQueue隊(duì)頭取出事件,匹配焦點(diǎn)窗口,通過(guò)mConnectionsByFd 獲取到對(duì)應(yīng)的Connection,將eventEntry轉(zhuǎn)化為DispatchEntry并加入到Connection的outboundQueue隊(duì)列,同時(shí)通過(guò)Connection獲取InputChannel將事件發(fā)送到客戶(hù)端,再將DispatchEntry從outboundQueue移到waitQueue里,等待客戶(hù)端完成處理反饋完成消息,而InputDispatcher收到消息后回調(diào)handleReceiveCallback,將DispatchEntry從waitQueue里移出。

ViewRootImpl: 調(diào)用NaitveInputEventReceiver的handleEvent()接收input事件,轉(zhuǎn)成java層對(duì)應(yīng)的事件,通過(guò)InputStage體系相關(guān)類(lèi)按責(zé)任鏈的模式進(jìn)行事件分發(fā)操作。事件分發(fā)處理完畢后會(huì)執(zhí)行finishInputEvent,通過(guò)InputChannel 發(fā)送完成的message,給到服務(wù)端InputDispatcher回調(diào)InputDispatcher.handleReceiveCallback()。

接下來(lái)解析Input ANR原理(代碼來(lái)源: Android 8.0)。

一、ANR觸發(fā)條件

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    //ANR計(jì)算的起點(diǎn)時(shí)間
    nsecs_t currentTime = now(); 
...
//如果正在處理的mPendingEvent為空才會(huì)進(jìn)來(lái)
if (! mPendingEvent) {
    if (mInboundQueue.isEmpty()) {
        if (isAppSwitchDue) {
            // The inbound queue is empty so the app switch key we were waiting
           // for will never arrive.  Stop waiting for it.
           resetPendingAppSwitchLocked(false);
            isAppSwitchDue = false;
        }
        // Synthesize a key repeat if appropriate.
       if (mKeyRepeatState.lastKeyEntry) {
            if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
            } else {
                if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                    *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                }
            }
        }
        // Nothing to do if there is no pending event.
       if (!mPendingEvent) {
            return;
        }
    } else {
        //從mInboundQueue頭部取出一個(gè)事件
       mPendingEvent = mInboundQueue.dequeueAtHead();
        traceInboundQueueLengthLocked();
    }
    // Poke user activity for this event.
   if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
        pokeUserActivityLocked(mPendingEvent);
    }
   resetANRTimeoutsLocked();
}
...
switch (mPendingEvent->type) {
  case EventEntry::TYPE_KEY: {
    KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
...
    done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
    break;
}
}
…
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
  }
}
void InputDispatcher::releasePendingEventLocked() {
    if (mPendingEvent) {
        resetANRTimeoutsLocked();
        releaseInboundEventLocked(mPendingEvent);
        mPendingEvent = NULL;
    }
}
void InputDispatcher::resetANRTimeoutsLocked() {
  // 這里reset等待超時(shí)原因和handle
   mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
   mInputTargetWaitApplicationHandle.clear();
}

InputDispatcher從dispatchOnceInnerLocked開(kāi)始事件的分發(fā)流程:

  • 先獲取anr計(jì)算的起點(diǎn)時(shí)間
  • 獲取事件,mPendingEvent不等于空才會(huì)進(jìn)獲取事件邏輯,mInboundQueue不為空的情況下,從頭部取出事件,通過(guò)resetANRTimeoutsLocked來(lái)reset等待超時(shí)原因和handle。
  • 針對(duì)不同類(lèi)型進(jìn)行對(duì)應(yīng)的事件分發(fā)
  • 事件處理完之后通過(guò)releasePendingEventLocked重置mPendingEvent = NULL;

因此前一個(gè)事件沒(méi)有處理完,下一次分發(fā)是不會(huì)進(jìn)入到獲取事件邏輯的。

繼續(xù)跟dispatchKeyLocked流程:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
…        //尋找焦點(diǎn)窗口
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
...
    //分發(fā)事件
   dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

再看findFocusedWindowTargetsLocked

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
        const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
...
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;
    }
    ALOGI("Dropping event because there is no focused window or focused application.");
    injectionResult = INPUT_EVENT_INJECTION_FAILED;
    goto Failed;
}
…
// Check whether the window is ready for more input.
reason = checkWindowReadyForMoreInputLocked(currentTime,
        mFocusedWindowHandle, entry, "focused");
...
if (!reason.isEmpty()) {
    injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
            mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
    goto Unresponsive;
}
...
   return injectionResult;
}

這里先看checkWindowReadyForMoreInputLocked,這個(gè)方法主要是針對(duì)window還沒(méi)有準(zhǔn)備好的情況,收集對(duì)應(yīng)的reason:

String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
        const char* targetType) {
       //窗口暫停
   if (windowHandle->getInfo()->paused) {
        return String8::format("Waiting because the %s window is paused.", targetType);
    }
   ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
    //窗口未連接
    if (connectionIndex < 0) {
        return String8::format("Waiting because the %s window's input channel is not "
               "registered with the input dispatcher.  The window may be in the process "
               "of being removed.", targetType);
    }
    //窗口連接已死亡
   sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
    if (connection->status != Connection::STATUS_NORMAL) {
        return String8::format("Waiting because the %s window's input connection is %s."
               "The window may be in the process of being removed.", targetType,
                connection->getStatusLabel());
    }
    //窗口連接已滿(mǎn)
   if (connection->inputPublisherBlocked) {
        return String8::format("Waiting because the %s window's input channel is full.  "
               "Outbound queue length: %d.  Wait queue length: %d.",
                targetType, connection->outboundQueue.count(), connection->waitQueue.count());
    }
   if (eventEntry->type == EventEntry::TYPE_KEY) {
        //按鍵事件,輸出隊(duì)列或事件等待隊(duì)列不為空
       if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
           return String8::format("Waiting to send key event because the %s window has not "
                   "finished processing all of the input events that were previously "
                   "delivered to it.  Outbound queue length: %d.  Wait queue length: %d.",
                   targetType, connection->outboundQueue.count(), connection->waitQueue.count());
       }
   } else {
       //非按鍵事件,事件等待隊(duì)列不為空且頭事件分發(fā)超時(shí)500ms
       if (!connection->waitQueue.isEmpty()
                && currentTime >= connection->waitQueue.head->deliveryTime
                        + STREAM_AHEAD_EVENT_TIMEOUT) {
            return String8::format("Waiting to send non-key event because the %s window has not "
                   "finished processing certain input events that were delivered to it over "
                   "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms.",
                    targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
                    connection->waitQueue.count(),
                    (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
        }
    }
    return String8::empty();
}

然后再看handleTargetsNotReadyLocked,這里currentTime作為參數(shù)一直從dispatchOnceInnerLocked傳遞到handleTargetsNotReadyLocked,這里是ANR計(jì)算的核心方法:

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
       const EventEntry* entry,
       const sp<InputApplicationHandle>& applicationHandle,
       const sp<InputWindowHandle>& windowHandle,
       nsecs_t* nextWakeupTime, const char* reason) {
   if (applicationHandle == NULL && windowHandle == NULL) {
       if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
#if DEBUG_FOCUS
           ALOGD("Waiting for system to become ready for input.  Reason: %s", reason);
#endif
           mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
           mInputTargetWaitStartTime = currentTime;
           mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
           mInputTargetWaitTimeoutExpired = false;
           mInputTargetWaitApplicationHandle.clear();
       }
   } else {
        //前面resetANRTimeoutsLocked執(zhí)行時(shí)reset mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE,所以能進(jìn)方法
       if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
           ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
                   getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
                   reason);
#endif
           nsecs_t timeout;
           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; //5S
           }
           mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
           mInputTargetWaitStartTime = currentTime;
           mInputTargetWaitTimeoutTime = currentTime + timeout; //當(dāng)前時(shí)間+5S
           mInputTargetWaitTimeoutExpired = false;
           mInputTargetWaitApplicationHandle.clear();
           if (windowHandle != NULL) {
               mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
           }
           if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
               mInputTargetWaitApplicationHandle = applicationHandle;
           }
       }
   }
   if (mInputTargetWaitTimeoutExpired) {
       return INPUT_EVENT_INJECTION_TIMED_OUT;
   }
    //當(dāng)前時(shí)間大于設(shè)置的超時(shí)時(shí)間就會(huì)走ANR觸發(fā)流程
   if (currentTime >= mInputTargetWaitTimeoutTime) {
       onANRLocked(currentTime, applicationHandle, windowHandle,
               entry->eventTime, mInputTargetWaitStartTime, reason);
       // Force poll loop to wake up immediately on next iteration once we get the
       // ANR response back from the policy.
       *nextWakeupTime = LONG_LONG_MIN;
       return INPUT_EVENT_INJECTION_PENDING;
   } else {
       // Force poll loop to wake up when timeout is due.
       if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
           *nextWakeupTime = mInputTargetWaitTimeoutTime;
       }
       return INPUT_EVENT_INJECTION_PENDING;
   }
}

可見(jiàn)ANR超時(shí)時(shí)間區(qū)間是從dispatchOnceInnerLocked()開(kāi)始,到下次執(zhí)行handleTargetsNotReadyLocked()這段應(yīng)用為準(zhǔn)備就緒的時(shí)間來(lái)決定是否觸發(fā)ANR。

二、ANR執(zhí)行流程

handleTargetsNotReadyLocked方法中,當(dāng)前時(shí)間大于設(shè)置的超時(shí)時(shí)間就會(huì)走ANR觸發(fā)流程onANRLocked。

void InputDispatcher::onANRLocked(
        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
    float dispatchLatency = (currentTime - eventTime) * 0.000001f;
    float waitDuration = (currentTime - waitStartTime) * 0.000001f;
    ALOGI("Application is not responding: %s.  "
           "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
            getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
            dispatchLatency, waitDuration, reason);
    // 收集ANR現(xiàn)場(chǎng)信息
   time_t t = time(NULL);
    struct tm tm;
    localtime_r(&t, &tm);
    char timestr[64];
    strftime(timestr, sizeof(timestr), "%F %T", &tm);
    mLastANRState.clear();
    mLastANRState.append(INDENT "ANR:\n");
    mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr);
    mLastANRState.appendFormat(INDENT2 "Window: %s\n",
            getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
    mLastANRState.appendFormat(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
    mLastANRState.appendFormat(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
    mLastANRState.appendFormat(INDENT2 "Reason: %s\n", reason);
    //dump信息
    dumpDispatchStateLocked(mLastANRState);
    //將ANR命令加入commandQueue
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible);
    commandEntry->inputApplicationHandle = applicationHandle;
    commandEntry->inputWindowHandle = windowHandle;
    commandEntry->reason = reason;
}

這里收集ANR現(xiàn)場(chǎng)信息并執(zhí)行dump操作,然后將ANR命令加入commandQueue,在下一輪InputDispatcher.dispatchOnce中是先通過(guò)runCommandsLockedInterruptible()方法取出 mCommandQueue隊(duì)列的所有命令逐一執(zhí)行。ANR對(duì)應(yīng)的command為doNotifyANRLockedInterruptible。

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
    //mPolicy是指NativeInputManager
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
            commandEntry->reason);
    mLock.lock();
    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputWindowHandle != NULL
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}

這里調(diào)用NativeInputManager的notifyANR方法。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {
...
    //jni調(diào)用java方法
    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
                reasonObj);
...
    return newTimeout;
}

這里通過(guò)JNI調(diào)用InputManagerService執(zhí)行notifyANR。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

// Native callback.
private long notifyANR(InputApplicationHandle inputApplicationHandle,
       InputWindowHandle inputWindowHandle, String reason) {
    return mWindowManagerCallbacks.notifyANR(
            inputApplicationHandle, inputWindowHandle, reason);
}

此處mWindowManagerCallbacks是指InputMonitor對(duì)象。

public long notifyANR(InputApplicationHandle inputApplicationHandle,
       InputWindowHandle inputWindowHandle, String reason) {
    AppWindowToken appWindowToken = null;
   WindowState windowState = null;
...
    // All the calls below need to happen without the WM lock held since they call into AM.
   mService.mAmInternal.saveANRState(reason);
…
    else if (windowState != null) {
        try {
            // Notify the activity manager about the timeout and let it decide whether
           // to abort dispatching or keep waiting.
           long timeout = ActivityManager.getService().inputDispatchingTimedOut(
                    windowState.mSession.mPid, aboveSystem, reason);
...
}

這里通過(guò)token打通對(duì)應(yīng)Activity與Window的聯(lián)系,最終調(diào)用AMS的inputDispatchingTimedOut

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
            != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Requires permission "
               + android.Manifest.permission.FILTER_EVENTS);
   }
    ProcessRecord proc;
   long timeout;
   synchronized (this) {
        synchronized (mPidsSelfLocked) {
            proc = mPidsSelfLocked.get(pid);
       }
        timeout = getInputDispatchingTimeoutLocked(proc);
   }
    if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
        return -1;
   }
    return timeout;
}

最終調(diào)用inputDispatchingTimedOut

public boolean inputDispatchingTimedOut(final ProcessRecord proc,
       final ActivityRecord activity, final ActivityRecord parent,
       final boolean aboveSystem, String reason) {
...
    if (proc != null) {
 ...
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
           }
        });
   }
    return true;
}

通過(guò)handler機(jī)制,交由“ActivityManager”線(xiàn)程執(zhí)行ANR處理過(guò)程, appNotResponding會(huì)dump信息以及彈窗提示ANR等。

frameworks/base/services/core/java/com/android/server/am/AppErrors.java
    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
      ...
        // For background ANRs, don't pass the ProcessCpuTracker to
        // avoid spending 1/2 second collecting stats to rank lastPids.
        //dump信息
        File tracesFile = mService.dumpStackTraces(true, firstPids,
                                                   (isSilentANR) ? null : processCpuTracker,
                                                   (isSilentANR) ? null : lastPids,
                                                   nativePids);
...
        //DropBox收集信息
        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
...

            // 彈App Not Responding dialog
            Message msg = Message.obtain();
            HashMap<String, Object> map = new HashMap<String, Object>();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }
            mService.mUiHandler.sendMessage(msg);
        }
    }

最后,可以通過(guò)adb shell dumpsys input來(lái)查看手機(jī)當(dāng)前的input狀態(tài), 輸出內(nèi)容分別為EventHub.dump(), InputReader.dump(),InputDispatcher.dump()這3類(lèi),另外如果發(fā)生過(guò)input ANR,那么也會(huì)輸出上一個(gè)ANR的狀態(tài)。

最后借輝輝一張圖:

Input ANR觸發(fā)流程

ANR原理分析總結(jié):
InputReader線(xiàn)程通過(guò)EventHub監(jiān)聽(tīng)底層上報(bào)的輸入事件,一旦收到輸入事件則將其放至mInBoundQueue隊(duì)列,并喚醒InputDispatcher線(xiàn)程

InputDispatcher開(kāi)始分發(fā)輸入事件,設(shè)置ANR監(jiān)控的起點(diǎn)時(shí)間。先檢測(cè)是否有正在處理的事件:mPendingEvent,如果沒(méi)有則從mInBoundQueue隊(duì)頭取事件,并將其賦值給mPendingEvent,且重置ANR的timeout;否則不會(huì)從mInBoundQueue中取出事件,也不會(huì)重置timeout。然后檢查窗口是否就緒:checkWindowReadyForMoreInputLocked,
滿(mǎn)足以下任一情況,則會(huì)進(jìn)入ANR檢測(cè)狀態(tài):

  • 窗口暫停
  • 窗口未連接
  • 窗口連接已死亡
  • 窗口連接已滿(mǎn)
  • 按鍵事件,outboundQueue或者waitQueue不為空
  • 非按鍵事件,waitQueue不為空且頭事件分發(fā)超時(shí)500ms

若窗口未準(zhǔn)備就緒,即滿(mǎn)足以上條件:
就是窗口還沒(méi)準(zhǔn)備好跟你進(jìn)行socket通信,那么你就只能等待,這個(gè)時(shí)候如果滿(mǎn)足ANR超時(shí)時(shí)間會(huì)觸發(fā)ANR。收集ANR信息,生成doNotifyANRLockedInterruptible加入commandQueue中,下次執(zhí)行InputDispatcher::dispatchOnce 時(shí)候,先執(zhí)行command,走notifyANR
流程,最終通過(guò)AMS發(fā)送消息來(lái)執(zhí)行ANR處理過(guò)程:appNotResponding會(huì)dump信息以及彈窗提示ANR等。

若窗口準(zhǔn)備就緒,連接成功:
則將mPendingEvent轉(zhuǎn)移到outBoundQueue隊(duì)列,當(dāng)outBoundQueue不為空,且應(yīng)用管道對(duì)端連接狀態(tài)正常,則將數(shù)據(jù)從outboundQueue中取出事件,放入waitQueue隊(duì)列,InputDispatcher通過(guò)socket告知目標(biāo)應(yīng)用所在進(jìn)程可以準(zhǔn)備開(kāi)始干活,App在初始化時(shí)默認(rèn)已創(chuàng)建跟中控系統(tǒng)雙向通信的socketpair,此時(shí)App的主線(xiàn)程收到輸入事件后,會(huì)層層轉(zhuǎn)發(fā)到目標(biāo)窗口來(lái)處理。完成工作后,會(huì)通過(guò)socket向中控系統(tǒng)匯報(bào)工作完成,則中控系統(tǒng)會(huì)將該事件從waitQueue隊(duì)列中移除。

下一篇文章:
Android Input(九)-Input問(wèn)題分析指北

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容