ANR即Application Not Responding, 应用无响应:当应用在某一个时间内无法及时响应用户请求时,系统会弹出一个对话框,告知用户应用无法响应。此时,用户可以选择关闭应用。对于Android应用来说,有一个UI线程(主线程)专门负责与用户交互,如果在此线程中进行耗时的操作,比如读取磁盘数据、从网络下载资源等IO操作,通常会导致UI线程阻塞,从而无法及时处理用户的输入请求,发生ANR。
Android是通过AMS跟WMS来监控应用响应状态的,一般有如下两种ANR情况:
应用在给定时间内没有对用户输入(按键或者触屏操作)做出响应,通常是5s;
广播接收者(BroadcastReceiver)没有在10s内处理完成;
对于输入无响应的情况,native监控到输入无响应时,则告知WMS,最后由WMS发送消息告诉AMS,某个应用发送了ANR,最后AMS会弹出Application Not Responding 的对话框,请求用户关闭应用;对于广播无响应的情况是由AMS负责处理的,AMS对于每个发送出去给广播接收者的广播都有一个10s的定时,如果广播接收者在10s内尚未处理完,则视为无响应,因此也会弹出Application Not Responding 。
首先来看下第一种情况。
输入无响应 输入无响应是指用户长时间(5s内)无法响应用户的输入操作,如触摸,按键等,此时AMS会弹出对话框提示用户应用出现ANR(Application Not Responding)。
Android由InputManagerService
(IMS)负责处理用户的输入事件,IMS会将输入转交给对应的应用窗口,最后由应用本身做出相应的操作;在native层,IMS有一个对应的InputManager
来管理来自Kernel的输入事件(有关Android输入系统,请参考罗升阳Android应用程序键盘消息处理机制分析 ),InputReader
负责读取输入事件,而InputDispatcher
则负责将读取的事件分发出去。这里,就来看下InputDispatcher
是如何检测应用输入无响应以及将其分发出去的。
以下是输入ANR处理流程图(图片来自http://gityuan.com/images/input/input_reader_seq.jpg):
native的InputManager
初始化时启动一个InputDispatcherThread
用于输入事件的分发:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 (); } .... void InputManager::initialize () { mReaderThread = new InputReaderThread (mReader); mDispatcherThread = new InputDispatcherThread (mDispatcher); } status_t InputManager::start () { status_t result = mDispatcherThread->run ("InputDispatcher" , PRIORITY_URGENT_DISPLAY); result = mReaderThread->run ("InputReader" , PRIORITY_URGENT_DISPLAY); return OK; }
InputDispatcherThread
启动时,进入一个线程循环:
1 2 3 4 5 6 7 8 9 10 11 12 InputDispatcherThread::InputDispatcherThread (const sp<InputDispatcherInterface>& dispatcher) : Thread ( true ), mDispatcher (dispatcher) { } bool InputDispatcherThread::threadLoop () { mDispatcher->dispatchOnce (); return true ; }
InputDispatcher
开始事件处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void InputDispatcher::dispatchOnce () { nsecs_t nextWakeupTime = LONG_LONG_MAX; { AutoMutex _l(mLock); mDispatcherIsAliveCondition.broadcast (); if (!haveCommandsLocked ()) { dispatchOnceInnerLocked (&nextWakeupTime); } if (runCommandsLockedInterruptible ()) { nextWakeupTime = LONG_LONG_MIN; } } nsecs_t currentTime = now (); int timeoutMillis = toMillisecondTimeoutDelay (currentTime, nextWakeupTime); mLooper->pollOnce (timeoutMillis); }
根据输入事件的类型将事件分发出去,按键输入事件类型是TYPE_KEY
,触屏输入事件TYPE_MOTION
,这里假定应用处理的是一个按键事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void InputDispatcher::dispatchOnceInnerLocked (nsecs_t * nextWakeupTime) { ... switch (mPendingEvent->type) { .... case EventEntry::TYPE_KEY: { ... done = dispatchKeyLocked (currentTime, typedEntry, &dropReason, nextWakeupTime); break ; } case EventEntry::TYPE_MOTION: { .... done = dispatchMotionLocked (currentTime, typedEntry, &dropReason, nextWakeupTime); break ; } } }
分发键盘事件之前,首先需要找到当前响应输入的焦点窗口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bool InputDispatcher::dispatchKeyLocked (nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t * nextWakeupTime) { .... Vector<InputTarget> inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked (currentTime, entry, inputTargets, nextWakeupTime); .... dispatchEventLocked (currentTime, entry, inputTargets); return true ; }
在查找目标焦点窗口之前,会检查是否有应用程序的窗口处于无响应状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int32_t InputDispatcher::findFocusedWindowTargetsLocked (nsecs_t currentTime, EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t * nextWakeupTime) { .... reason = checkWindowReadyForMoreInputLocked (currentTime, focusedWindowHandle, entry, "focused" ); if (!reason.isEmpty ()) { injectionResult = handleTargetsNotReadyLocked (currentTime, entry, mFocusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.string ()); goto Unresponsive; } Failed: Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked (currentTime); updateDispatchStatisticsLocked (currentTime, entry, injectionResult, timeSpentWaitingForApplication); return injectionResult; }
如果发现焦点窗口无法对用户事件作出响应,并且超过了给定的时间(Android设定的超时为5s),则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked (currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason); *nextWakeupTime = LONG_LONG_MIN; return INPUT_EVENT_INJECTION_PENDING; } else { if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { *nextWakeupTime = mInputTargetWaitTimeoutTime; } return INPUT_EVENT_INJECTION_PENDING; } }
将ANR事件以CommandEntry
的形式加入到命令队列中,等到下一次输入处理周期时在处理,处理时会调用函数doNotifyANRLockedInterruptible
(见dispatchOnce()
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void InputDispatcher::onANRLocked ( nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char * reason) { ... CommandEntry* commandEntry = postCommandLocked ( & InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputWindowHandle = windowHandle; commandEntry->reason = reason; }
通过InputDispatcherPolicyInterface
接口通知NativeInputMananger
有ANR事件发生:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void InputDispatcher::doNotifyANRLockedInterruptible ( CommandEntry* commandEntry) { mLock.unlock (); nsecs_t newTimeout = mPolicy->notifyANR ( commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle, commandEntry->reason); mLock.lock (); resumeAfterTargetsNotReadyTimeoutLocked (newTimeout, commandEntry->inputWindowHandle != NULL ? commandEntry->inputWindowHandle->getInputChannel () : NULL ); }
到这里,准备通知InputManagerService.java
(IMS)ANR事件了,mServiceObj
实际是一个Java类InputManagerService
的一个全局引用,通过CallLongMethod
来调用IMS中的notifyANR
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 nsecs_t NativeInputManager::notifyANR (const sp<InputApplicationHandle>& inputApplicationHandle, const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) { JNIEnv* env = jniEnv (); jobject inputApplicationHandleObj = getInputApplicationHandleObjLocalRef (env, inputApplicationHandle); jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef (env, inputWindowHandle); jstring reasonObj = env->NewStringUTF (reason.string ()); jlong newTimeout = env->CallLongMethod (mServiceObj, gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj, reasonObj); env->DeleteLocalRef (reasonObj); env->DeleteLocalRef (inputWindowHandleObj); env->DeleteLocalRef (inputApplicationHandleObj); return newTimeout; }
NativeInputManager
位于com_android_server_input_InputManagerService.cpp
,是InputManagerService.java
对应的Native文件中的类,其负责初始化输入系统的native框架
IMS收到ANR事件后,通过一个回调接口WindowManagerCallbacks
通知WMS(WindowManagerService), WindowManagerCallbacks
接口实际是一个InputMonitor.java
对象,在SystemServer进程启动时,将WMS中的InputMonitor
设置为IMS的回调:
1 2 3 4 5 6 7 8 private long notifyANR (InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, inputWindowHandle, reason); }
向AMS发起inputDispatchingTimedOut
的Binder请求,告知其ANR事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public long notifyANR (InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { .... if (appWindowToken != null && appWindowToken.appToken != null ) { } else if (windowState != null ) { try { long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); if (timeout >= 0 ) { return timeout * 1000000L ; } } catch (RemoteException ex) { } } return 0 ; }
AMS处理输入响应超时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override public long inputDispatchingTimedOut (int pid, final boolean aboveSystem, String reason) { 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; }
发现有应用进程ANR,AMS向主线程发送一个可执行对象,执行ANR操作,最后AppErrors
会向当前应用程序的UI线程发送一个ANR的对话框:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 ; }
接下来,再来看下广播处理超时的情况。
广播处理超时 广播的运行机制很像“内容发布者-订阅者”这样的信息传递方式:新闻报纸就是典型的例子,订阅者首先得到新闻通讯社注册成为用户才能接收到报纸;而新闻媒体则负责将报纸发送给各个合法的注册者。与此类似,广播的发送者通过Context
中的接口sendBroadcast(Intent)
向系统发送广播;而接收者则需要通过Context
中提供的接口registerReceiver
来监听广播,对于有系统权限限制的广播,需要相应的用户权限才允许接收。
Android系统广播由AMS(ActivityManagerService)负责管理,AMS中有两个广播队列,一个是前台广播(Intent中对应的标志位FLAG_RECEIVER_FOREGROUND
),具有在前台运行的优先级;一个是后台广播,不指定前台标志位的广播都默认为后台广播。对于前台广播,接收者如果在10s内为完成,则视为超时;后台广播如果接收后60s内为完成,则超时。此时,AMS会向当前UI线程发送一个ANR的对话框。
AMS中接收到广播后,将广播放入一个队列,根据广播接收者注册机制的不同,广播队列分为并行与串行两种处理方式,并行处理广播不用等待其他广播完成,主要用于处理用Context
接口注册的广播接收者;串行方式则必须按照严格广播入队列的先后次序来处理.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 final int broadcastIntentLocked (ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { .... int NR = registeredReceivers != null ? registeredReceivers.size() : 0 ; if (!ordered && NR > 0 ) { if (isCallerSystem) { checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, isProtectedBroadcast, registeredReceivers); } final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord (queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false , userId); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); if (!replaced) { queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } registeredReceivers = null ; NR = 0 ; } if ((receivers != null && receivers.size() > 0 ) || resultTo != null ) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord (queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } } else { .... } } return ActivityManager.BROADCAST_SUCCESS; }
发送消息BROADCAST_INTENT_MSG
请求处理广播:
1 2 3 4 5 6 7 8 9 10 11 public void scheduleBroadcastsLocked () { if (mBroadcastsScheduled) { return ; } mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this )); mBroadcastsScheduled = true ; }
Handler
接受到消息,开始处理队列中的广播:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private final class BroadcastHandler extends Handler { public BroadcastHandler (Looper looper) { super (looper, null , true ); } @Override public void handleMessage (Message msg) { switch (msg.what) { case BROADCAST_INTENT_MSG: { processNextBroadcast(true ); } break ; case BROADCAST_TIMEOUT_MSG: { synchronized (mService) { broadcastTimeoutLocked(true ); } } break ; } } }
处理下一个广播,简单起见,这里只分析串行分发广播的方式:首先记录下广播分发的时间dispatchTime
,接着设置广播超时时间;最后开始处理广播,将其发送给广播接收者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 final void processNextBroadcast (boolean fromMsg) { synchronized (mService) { do { r = mOrderedBroadcasts.get(0 ); cancelBroadcastTimeoutLocked(); } while (r == null ); ... r.receiverTime = SystemClock.uptimeMillis(); if (recIdx == 0 ) { r.dispatchTime = r.receiverTime; r.dispatchClockTime = System.currentTimeMillis(); } if (! mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); } .... if (app != null && app.thread != null ) { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); processCurBroadcastLocked(r, app); return ; } catch (RemoteException e) { } catch (RuntimeException e) { logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false ); scheduleBroadcastsLocked(); r.state = BroadcastRecord.IDLE; return ; } } if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true , r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast" , r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0 , false , false , false )) == null ) { finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false ); scheduleBroadcastsLocked(); r.state = BroadcastRecord.IDLE; return ; } }
如果广播处理在10s内未完成,则出发TIME_OUT的消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 final void broadcastTimeoutLocked (boolean fromMsg) { if (fromMsg) { mPendingBroadcastTimeoutMessage = false ; } if (mOrderedBroadcasts.size() == 0 ) { return ; } long now = SystemClock.uptimeMillis(); BroadcastRecord r = mOrderedBroadcasts.get(0 ); if (fromMsg) { long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) { setBroadcastTimeoutLocked(timeoutTime); return ; } } .... r.receiverTime = now; r.anrCount++; ProcessRecord app = null ; String anrMessage = null ; Object curReceiver = r.receivers.get(r.nextReceiver-1 ); r.delivery[r.nextReceiver-1 ] = BroadcastRecord.DELIVERY_TIMEOUT; if (curReceiver instanceof BroadcastFilter) { BroadcastFilter bf = (BroadcastFilter)curReceiver; if (bf.receiverList.pid != 0 && bf.receiverList.pid != ActivityManagerService.MY_PID) { synchronized (mService.mPidsSelfLocked) { app = mService.mPidsSelfLocked.get( bf.receiverList.pid); } } } else { app = r.curApp; } finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false ); scheduleBroadcastsLocked(); if (anrMessage != null ) { mHandler.post(new AppNotResponding (app, anrMessage)); } }