ANR机制

ANR全称是application not responsing,也就是程序无响应。在android系统中,和程序崩溃一样,都是程序设计编码的问题。 android应用中,当你的touch、key等事件得不到及时响应时,在一定的延时之后,系统就会弹出ANR的警告。此时,用户可以选择结束程序或者继续等待。本文将主要从分析ANR原理,以及如何避免ANR。

一、ANR产生的原因

在android程序中,所有的输入(key和touch等)事件是由底层的InputDispatcher分发到上层的InputManagerService的,再通过InputManagerService内部的InputMonitor送入WindowManagerService的Policy(PhoneWindowManager)中。整个流程可以参考事件在native和jni中的流程事件在java framework中的流程。而在事件分发的过程中,如果应用程序不能及时响应,就会产生ANR。

以下是一些主要的代码片段

/frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp(java和native的对接桥梁)

// java层的InputManagerService初始化的时候,会调用jni方法nativeInit(),而该方法会初始化
// NativeInputManager,并最终创建native层的InputManager对象。创建InputManager对象
// 时,传递了3个参数,分别是event hub、read policy和dispatch policy。而这个dispatch 
// policy最终会传递到InputDispatcher中,并作为其mPolicy变量保存下来。所以,InputDispatcher
// 里的事件,最终通过这个NativeInputManager对象送入了java层的InputManagerService。
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj); // java层的Context
    mServiceObj = env->NewGlobalRef(serviceObj); // java层的InputManagerService

    {
        AutoMutex _l(mLock);

        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }

    sp<EventHub> eventHub = new EventHub();

    // 三个参数分别是event hub、read policy、dispatch policy
    mInputManager = new InputManager(eventHub, this, this);
}

/frameworks/base/services/input/InputDispatcher.cpp(底层事件分发到java层)

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);

    // Capture a record of the InputDispatcher state at the time of the ANR.
    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);
    dumpDispatchStateLocked(mLastANRState);

    // 将一个Command存入一个队列中,而该命令队列会在未来某个时间点执行,也就是执行  
    // doNotifyANRLockedInterruptible()方法了
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible);
    commandEntry->inputApplicationHandle = applicationHandle;
    commandEntry->inputWindowHandle = windowHandle;
}

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();

    // 这个mPolicy就是NativeInputManager对象了,最终再由NativeInputManager调用到
    // InputManagerService.java的notifyANR()
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle);

    mLock.lock();

    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputWindowHandle != NULL
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
        const EventEntry* entry,
        const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t* nextWakeupTime, const char* reason) {

    // 一般是系统error时出现,比如无法找到焦点window等
    if (applicationHandle == NULL && windowHandle == NULL) {
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
            // 记录事件超时的相关属性
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
            mInputTargetWaitTimeoutExpired = false;
            mInputTargetWaitApplicationHandle.clear();
        }
    } else { // 应用程序ANR时,一般情况下window和application都是存在的
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            nsecs_t timeout;
            if (windowHandle != NULL) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); // 默认是5s
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(
                        DEFAULT_INPUT_DISPATCHING_TIMEOUT); // 默认是5s
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
            }

            // 记录事件超时的相关属性
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            mInputTargetWaitTimeoutTime = currentTime + timeout;
            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;
    }

    // 事件超时
    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;
    }
}

/frameworks/base/services/java/com/android/server/input/InputManagerService.java
/frameworks/base/services/java/com/android/server/wm/InputMonitor.java

    // =============== InputManager.java ===============
    // Native callback.
    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle) {
        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
    }  


    // =============== InputMonitor.java ===============
    /* Notifies the window manager about an application that is not responding.
     * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
     * 
     * Called by the InputManager.
     */
    public long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle) {
        AppWindowToken appWindowToken = null;
        WindowState windowState = null;
        boolean aboveSystem = false;
        synchronized (mService.mWindowMap) {
            // 。。。。省略

            // 存储ANR的相关信息
            mService.saveANRStateLocked(appWindowToken, windowState);
        }

        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) {
            }
        } 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 = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
                        windowState.mSession.mPid, aboveSystem);
                if (timeout >= 0) {
                    // The activity manager declined to abort dispatching.
                    // Wait a bit longer and timeout again later.
                    return timeout;
                }
            } catch (RemoteException ex) {
            }
        }
        return 0; // abort dispatching
    }


    // =============== ActivityManagerService.java ===============
    public long inputDispatchingTimedOut(int pid, boolean aboveSystem) {
        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.FILTER_EVENTS);
        }

        ProcessRecord proc;

        // 。。。。省略

        if (proc != null) {
            appNotResponding(proc, null, null, aboveSystem, "keyDispatchingTimedOut");
            if (proc.instrumentationClass != null || proc.usingWrapper) {
                return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
            }
        }

        return KEY_DISPATCHING_TIMEOUT;
    }

    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
        // 。。。。省略

            // Bring up the infamous App Not Responding dialog
            Message msg = Message.obtain();
            HashMap map = new HashMap();
            msg.what = SHOW_NOT_RESPONDING_MSG;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }

            // 该消息最终会在mHandler中处理,发布一个android.intent.action.ANR的广播,
            // 并且显示一个AppNotRespondingDialog
            mHandler.sendMessage(msg);
    }

二、如何避免ANR

在主线程里的尽可能少做事情。特别是在Acitivty的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作;或者使用AsyncTask、ThreadPool等方法。如果有必要,可以开启多进程的service,将一些耗时操作放到service里处理。另外,需要注意的,不管网络请求还是本地的IO,都是不允许在主线程里做的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值