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 |
|
InputDispatcherThread
启动时,进入一个线程循环:
1 |
|
InputDispatcher
开始事件处理:
1 |
|
根据输入事件的类型将事件分发出去,按键输入事件类型是TYPE_KEY
,触屏输入事件TYPE_MOTION
,这里假定应用处理的是一个按键事件:
1 |
|
分发键盘事件之前,首先需要找到当前响应输入的焦点窗口:
1 |
|
在查找目标焦点窗口之前,会检查是否有应用程序的窗口处于无响应状态:
1 |
|
如果发现焦点窗口无法对用户事件作出响应,并且超过了给定的时间(Android设定的超时为5s),则
1 |
|
将ANR事件以CommandEntry
的形式加入到命令队列中,等到下一次输入处理周期时在处理,处理时会调用函数doNotifyANRLockedInterruptible
(见dispatchOnce()
):
1 |
|
通过InputDispatcherPolicyInterface
接口通知NativeInputMananger
有ANR事件发生:
1 |
|
到这里,准备通知InputManagerService.java
(IMS)ANR事件了,mServiceObj
实际是一个Java类InputManagerService
的一个全局引用,通过CallLongMethod
来调用IMS中的notifyANR
方法:
1 |
|
NativeInputManager
位于com_android_server_input_InputManagerService.cpp
,是InputManagerService.java
对应的Native文件中的类,其负责初始化输入系统的native框架
IMS收到ANR事件后,通过一个回调接口WindowManagerCallbacks
通知WMS(WindowManagerService), WindowManagerCallbacks
接口实际是一个InputMonitor.java
对象,在SystemServer进程启动时,将WMS中的InputMonitor
设置为IMS的回调:
1 |
|
向AMS发起inputDispatchingTimedOut
的Binder请求,告知其ANR事件:
1 |
|
AMS处理输入响应超时:
1 |
|
发现有应用进程ANR,AMS向主线程发送一个可执行对象,执行ANR操作,最后AppErrors
会向当前应用程序的UI线程发送一个ANR的对话框:
1 |
|
接下来,再来看下广播处理超时的情况。
广播处理超时
广播的运行机制很像“内容发布者-订阅者”这样的信息传递方式:新闻报纸就是典型的例子,订阅者首先得到新闻通讯社注册成为用户才能接收到报纸;而新闻媒体则负责将报纸发送给各个合法的注册者。与此类似,广播的发送者通过Context
中的接口sendBroadcast(Intent)
向系统发送广播;而接收者则需要通过Context
中提供的接口registerReceiver
来监听广播,对于有系统权限限制的广播,需要相应的用户权限才允许接收。
Android系统广播由AMS(ActivityManagerService)负责管理,AMS中有两个广播队列,一个是前台广播(Intent中对应的标志位FLAG_RECEIVER_FOREGROUND
),具有在前台运行的优先级;一个是后台广播,不指定前台标志位的广播都默认为后台广播。对于前台广播,接收者如果在10s内为完成,则视为超时;后台广播如果接收后60s内为完成,则超时。此时,AMS会向当前UI线程发送一个ANR的对话框。
AMS中接收到广播后,将广播放入一个队列,根据广播接收者注册机制的不同,广播队列分为并行与串行两种处理方式,并行处理广播不用等待其他广播完成,主要用于处理用Context
接口注册的广播接收者;串行方式则必须按照严格广播入队列的先后次序来处理.
1 |
|
发送消息BROADCAST_INTENT_MSG
请求处理广播:
1 |
|
Handler
接受到消息,开始处理队列中的广播:
1 |
|
处理下一个广播,简单起见,这里只分析串行分发广播的方式:首先记录下广播分发的时间dispatchTime
,接着设置广播超时时间;最后开始处理广播,将其发送给广播接收者:
1 |
|
如果广播处理在10s内未完成,则出发TIME_OUT的消息:
1 |
|