Android按键超时的ANR原理小结

来源:互联网 发布:excel数据验证限制 编辑:程序博客网 时间:2024/05/29 03:19

1) 按键处理主要的功臣是WindowManagerService. mInputManager

@WindowManagerService.java

mInputManager = new InputManager(context, this);

 

@InputManager.java

    

 public InputManager(Context context, WindowManagerService windowManagerService) {        this.mContext = context;        this.mWindowManagerService = windowManagerService;        this.mCallbacks = new Callbacks();        Looper looper = windowManagerService.mH.getLooper();        Slog.i(TAG, "Initializing input manager");        nativeInit(mContext, mCallbacks, looper.getQueue());        // Add ourself to the Watchdog monitors.        Watchdog.getInstance().addMonitor(this);    }


@com_android_server_InputManager.cpp


static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,        jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {    if (gNativeInputManager == NULL) {        sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);        gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);    } else {        LOGE("Input manager already initialized.");        jniThrowRuntimeException(env, "Input manager already initialized.");    }}NativeInputManager::NativeInputManager(jobject contextObj,        jobject callbacksObj, const sp<Looper>& looper) :        mLooper(looper) {    JNIEnv* env = jniEnv();    mContextObj = env->NewGlobalRef(contextObj);    mCallbacksObj = env->NewGlobalRef(callbacksObj);    {        AutoMutex _l(mLock);        mLocked.displayWidth = -1;        mLocked.displayHeight = -1;        mLocked.displayExternalWidth = -1;        mLocked.displayExternalHeight = -1;        mLocked.displayOrientation = DISPLAY_ORIENTATION_0;        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;        mLocked.pointerSpeed = 0;        mLocked.pointerGesturesEnabled = true;        mLocked.showTouches = false;    }    sp<EventHub> eventHub = new EventHub();    mInputManager = new InputManager(eventHub, this, this);}

@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();}


2) 接下来,重点来了,inputDispatcher负责事件(keytouch)的分发,而事件处理延时的ANR也在它这里。InputReader主要负责读取底层传上来的事件,这里就不介绍了

@inputDispatcher.cpp


// If the currently focused window is still working on previous events then keep waiting.    if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindowHandle)) {#if DEBUG_FOCUS        LOGD("Waiting because focused window still processing previous input.");#endif        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,                mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime);        goto Unresponsive;    }...int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,        const EventEntry* entry,        const sp<InputApplicationHandle>& applicationHandle,        const sp<InputWindowHandle>& windowHandle,        nsecs_t* nextWakeupTime) {... if (currentTime >= mInputTargetWaitTimeoutTime) {        onANRLocked(currentTime, applicationHandle, windowHandle,                entry->eventTime, mInputTargetWaitStartTime);        // 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;    }}void InputDispatcher::onANRLocked(        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,        const sp<InputWindowHandle>& windowHandle,        nsecs_t eventTime, nsecs_t waitStartTime) {    LOGI("Application is not responding: %s.  "            "%01.1fms since event, %01.1fms since wait started",            getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),            (currentTime - eventTime) / 1000000.0,            (currentTime - waitStartTime) / 1000000.0);    CommandEntry* commandEntry = postCommandLocked(            & InputDispatcher::doNotifyANRLockedInterruptible);    commandEntry->inputApplicationHandle = applicationHandle;    commandEntry->inputWindowHandle = windowHandle;}void InputDispatcher::doNotifyANRLockedInterruptible(        CommandEntry* commandEntry) {    mLock.unlock();    nsecs_t newTimeout = mPolicy->notifyANR(            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle);    mLock.lock();    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,            commandEntry->inputWindowHandle != NULL                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);}

这里的mPlicy是在构造的时候初始化的。追溯前面的inputManager.cpp,com_android_server_InputManager.cpp,很快

发现mPlicy其实就是NativeInputManager。

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :    mPolicy(policy),

@ com_android_server_InputManager.cpp


nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,        const sp<InputWindowHandle>& inputWindowHandle) {#if DEBUG_INPUT_DISPATCHER_POLICY    LOGD("notifyANR");#endif    JNIEnv* env = jniEnv();    jobject inputApplicationHandleObj =            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);    jobject inputWindowHandleObj =            getInputWindowHandleObjLocalRef(env, inputWindowHandle);    jlong newTimeout = env->CallLongMethod(mCallbacksObj,                gCallbacksClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj);    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {        newTimeout = 0; // abort dispatch    } else {        assert(newTimeout >= 0);    }    env->DeleteLocalRef(inputWindowHandleObj);    env->DeleteLocalRef(inputApplicationHandleObj);    return newTimeout;}

3) 仔细回忆下前面从java层到native层的inputManger和inputDispatcher的构造,不难发现mPlicy的notifyANR()最终回到inputManager.java中对应Callbacks的notifyANR()。(JNI:不仅java层访问本地方法,同样在本地方法中也可以访问java层方法)

@inputManager.java


public long notifyANR(InputApplicationHandle inputApplicationHandle,                InputWindowHandle inputWindowHandle) {            return mWindowManagerService.mInputMonitor.notifyANR(                    inputApplicationHandle, inputWindowHandle);        }

@WindowManagerService.java 


final InputMonitor mInputMonitor = new InputMonitor(this);

@InputMonitor.java


public long notifyANR(InputApplicationHandle inputApplicationHandle,            InputWindowHandle inputWindowHandle) {        AppWindowToken appWindowToken = null;        if (inputWindowHandle != null) {            synchronized (mService.mWindowMap) {                WindowState windowState = (WindowState) inputWindowHandle.windowState;                if (windowState != null) {                    Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "                            + windowState.mAttrs.getTitle());                    appWindowToken = windowState.mAppToken;                }            }        }                if (appWindowToken == null && inputApplicationHandle != null) {            appWindowToken = inputApplicationHandle.appWindowToken;            if (appWindowToken != null) {                Slog.i(WindowManagerService.TAG,                        "Input event dispatching timed out sending to application "                                + appWindowToken.stringName);            }        }        if (appWindowToken != null && appWindowToken.appToken != null) {            try {                // Notify the activity manager about the timeout and let it decide whether                // to abort dispatching or keep waiting.                boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();                if (! abort) {                    // The activity manager declined to abort dispatching.                    // Wait a bit longer and timeout again later.                    return appWindowToken.inputDispatchingTimeoutNanos;                }            } catch (RemoteException ex) {            }        }        return 0; // abort dispatching    }class AppWindowToken extends WindowToken {    // Non-null only for application tokens.    final IApplicationToken appToken;

其实这里的appToken实质上是一个ActivityRecord的“代理”。(需了解binder)

@ActivityRecord.java


final class ActivityRecord {    final ActivityManagerService service; // owner    final ActivityStack stack; // owner    final IApplicationToken.Stub appToken; // window manager tokenpublic boolean keyDispatchingTimedOut() {        ActivityRecord r;        ProcessRecord anrApp = null;        synchronized(service) {            r = getWaitingHistoryRecordLocked();            if (r != null && r.app != null) {                if (r.app.debugging) {                    return false;                }                                if (service.mDidDexOpt) {                    // Give more time since we were dexopting.                    service.mDidDexOpt = false;                    return false;                }                                if (r.app.instrumentationClass == null) {                     anrApp = r.app;                } else {                    Bundle info = new Bundle();                    info.putString("shortMsg", "keyDispatchingTimedOut");                    info.putString("longMsg", "Timed out while dispatching key event");                    service.finishInstrumentationLocked(                            r.app, Activity.RESULT_CANCELED, info);                }            }        }                if (anrApp != null) {            service.appNotResponding(anrApp, r, this,                    "keyDispatchingTimedOut");        }                return true;    }

接下去就是ActivityManagerService的appNotResponding,我们平常所看到的ANR对话框正出自这里。

原创粉丝点击