ANR 分析(一)

ANR,是“Application Not Responding”的缩写,即“应用程序无响应”。
与Java Crash或者Native Crash不同,ANR并不会导致程序崩溃,如果用户愿意等待,大多数ANR在一段时间后都是可以恢复的。但对于用户而言,打开一个窗口就要黑屏8秒,或者按下一个按钮后10秒程序没有任何响应显然是不可接受的。

为了便于开发者Debug自己程序中响应迟缓的部分,Android提供了ANR机制。ActivityManagerService(简称 AMS)和 WindowManagerService(简称 WMS)会监测应用程序的响应时间,如果应用程序主线程(即 UI 线程)在超时时间内对输入事件没有处理完毕,或者对特定操作没有执行完毕,就会出现 ANR。

1. ANR发生的原因


一般地,ANR的产生需要同时满足三个条件:

  1. 主线程:只有应用程序进程的主线程响应超时才会产生ANR。因为只有主线程也就是UI线程需要与用户进行交互,子线程的阻塞或者缓慢只要不影响主线程就不会引发ANR。

  2. 超时时间:不同类型ANR的超时时间不同,只要主线程在这个时间上限内没有响应就会ANR。

  3. 输入事件/‘’’’特定操作:输入事件是指按键、触屏等设备输入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数,产生ANR的场景不同,报出ANR的原因也会不同。

  4. 如何理解“产生 ANR 的场景不同,报出ANR的原因也会不同”呢?

假设应用程序主线程被阻塞,如果用户点击屏幕,稍后会报出“用户输入事件处理超时”ANR;

  如果来了需要处理的广播,会导致“广播处理超时”;

  如果用户切换窗口,则可能导致“窗口获取焦点超时”。

  同一个阻塞的位置和原因,在不同情况下报出的ANR类型和现象可能是不同的。

  这就需要在分析过程中透过现象看本质,找到不同Bug共同的原因,从而准确、快速地处理。

2. ANR的类型


1. 用户输入事件处理超时

当应用程序的窗口处于活动状态并且能够接收输入事件(例如按键事件、触摸事件等)时,系统底层上报的事件就会被InputDispatcher分发给该应用程序。对大多数窗口而言“处于活动状态”可以理解为“获得焦点”,但是一些具有FLAG_NOT_FOCUSABLE属性的窗口,如Popup窗口,不能获得焦点不能接收按键事件只能接收触摸事件,使得这两个概念不能完全等价。

应用程序的主线程通过InputChannel读取输入事件并交给界面视图处理,界面视图是一个树状结构,DecorView是视图树的根,事件从树根开始一层一层向端点(例如一个 Button)传递。开发者通常需要注册监听器来接收并处理事件,或者创建自定义的视图控件来处理事件。

InputDispatcher运行在system_server进程的一个子线程中,每当接收到一个新的输入事件,InputDispatcher就会检测前一个已经发给应用程序的输入时间是否已经处理完毕,如果超时,会通过一系列的回调通知WMS的notifyANR函数报告ANR发生,

需要注意的是,产生这种ANR的前提是要有输入事件,如果没有输入事件,即使主线程阻塞了也不会报告ANR。从设计的角度看,此时系统会推测用户没有关注手机,寄希望于一段时间后阻塞会自行消失,因此会暂时“隐瞒不报”。从实现的角度看,InputDispatcher没有分发事件给应用程序,当然也不会检测处理超时和报告ANR了。

此类ANR发生时的提示语是:Reason: Input dispatching timed out (Waiting because the focused window has not finished processing the input events that were previously delivered to it.)需要注意区分同为Input dispatching timed out大类的窗口获取焦点超时,这两类超时括号内的提示语是不同的。

此类ANR的超时时间在ActivityManagerService.java中定义,默认为5秒。如果有需要可以修改代码将小内存设备上的超时时间改为8秒。另一个常见的修改是在手机启动后的4分钟内将超时时间暂时提高到15秒,因为开机后MediaServer扫描媒体数据库会消耗大量CPU,这样修改有助以提高Monkey测试时的首错时间。

2 . 窗口获取焦点超时

窗口获取焦点超时是用户输入事件处理超时的一种子类型,它们都由InputDispatcher向AMS上报。当应用程序的窗口处于“活动状态”并且能够接收输入事件时,系统底层上报的事件就会被InputDispatcher分发给该应用程序。如果由于某种原因,窗口迟迟不能达到“活动状态”,不能接收输入事件,此时InputDispatcher就会报出“窗口获取焦点超时”。

此类ANR发生时的提示语是:Reason: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)需要注意区分同为Input dispatching timed out大类的用户输入事件处理超时,这两类超时括号内的提示语是不同的。

为了研究窗口为什么会获取焦点超时,我们需要简单了解在窗口切换过程中焦点应用和焦点窗口的切换逻辑。假设当前正处于应用A中,将要启动应用B。启动过程中焦点应用和焦点窗口转换如下:

1. 流程开始,焦点应用是A,焦点窗口是A(的某一个窗口)。

    2. 当A开始OnPause流程后,焦点应用是A,焦点窗口是null。

    3. 在zygote创建B的进程完毕后,焦点应用是B,焦点窗口是null。

    4. 应用B的OnResume流程完成后,焦点应用是B,焦点窗口是B(的某一个窗口)。

在这个过程中,如果焦点窗口为 null’的时间超过了5’秒,那么当前焦点应用就会被报告为窗口获取焦点超时类的ANR’。’

需要注意的是会被报告为ANR的是“当前焦点应用”而不是B。理论上讲创建新应用进程的速度非常快,焦点应用总是能及时地切换到新应用B上,在理想情况下“当前焦点应用”和“新启动的应用B”是等价的。

可惜在实际操作中,某些情况下发生ANR时,被报出ANR的应用并不是真正发生ANR的应用。如果步骤3中zygote迟迟创建不出应用B的进程,那么焦点应用会一直保持在A上,超时后就会报出A发生ANR;此外Android4.4上为了适应多窗口逻辑的需要,WMS和InputDispatcher维护的焦点窗口和焦点应用可以不同步,且原生代码中存在Bug。比如在上述流程中,步骤3中应用B的进程创建完成,但是由于原生Bug导致焦点应用没有转换,超时后同样会报出A发生ANR。

因此在分析窗口获取焦点超时的ANR时,一定要注意分析当前焦点应用和焦点窗口是否一致,首先要明确ANR的真正应用是哪一个,后续的分析才会有价值。

窗口获取焦点超时通常由以下4类原因导致:

1. 应用程序创建慢。程序的OnCreate/OnStart/OnResume方法执行速度慢/存在死锁/死循环导致OnResume迟迟不能执行完毕,超时造成ANR。

2. 应用程序'OnPause'慢。对同一个应用而言,前一次OnPause执行完毕之前后一次OnResume不会执行。但不同应用之间不会互相影响。

3. 系统整体性能慢。由于系统性能原因,如CPU占用率高/平均等待队列长/内存碎片化/页错误高/GC慢/用户空间冻结/进程陷入不可打断的睡眠,会造成整体运行慢使ANR频繁发生。

4. 'WMS'异常。由于4.4上存在的原生Bug,有时应用OnResume执行完毕后8秒焦点仍然不会转换。导致ANR发生。
3 . 广播超时

当应用程序主线程在执行BroadcastReceiver的onReceive方法时,超时没有执行完毕,就会报出广播超时类型的ANR。对于前台进程超时时间是10秒,后台进程超时时间是60秒。如果需要完成一项比较耗时的工作,应当通过发送Intent给应用的Service来完成,而不应长时间占用OnReceive主线程。与前两类ANR不同,系统对这类ANR不会显示对话框提示,仅在slog中输出异常信息。

此类ANR发生时的提示语是:Reason: Broadcast of Intent { act=android.net.wifi.WIFI_STATE_CHANGED flg=0x4000010 cmp=com.android.settings/.widget.SettingsAppWidgetProvider (has extras) }

在小内存Android设备上,Kernel中的LowMemoryKiller会频繁地杀死一些后台应用以释放内存。如果一个应用恰好在开始执行OnReceive方法时被LMK杀死,那么在60秒后BoardcastQueue检查广播处理情况时此应用就一定会发生ANR。这种场景的关键特征是报出ANR时System.log中会显示ANR应用的PID为0。

为避免此类问题发生,提高Monkey测试首错时间,可以在BoardcastQueue中添加代码,检测广播超时ANR的PID,为0时不报ANR。

4 . Service 执行超时

Service 的各个生命周期函数,如OnStart、OnCreate、OnStop也运行在主线程中,当这些函数超过 20 秒钟没有返回就会触发 ANR。同样对这种情况的 ANR 系统也不会显示对话框提示,仅是输出 log。

此类ANR的提示语是:Reason: Executing service com.android.bluetooth/.btservice. AdapterService

5 . ContentProvider执行超时

在Android4.4后继平台上,为方便ContentProvider进行Debug增加了此类ANR。当主线程在执行ContentProvider相关操作时没有在规定的时间内执行完毕就会发生ANR。由程序开发者自行设置是否启用以及超时时间。ANR发生时的提示语为:Reason: ContentProvider not responding。

调用方法可以参考ContentProviderClient.java

beforeRemote() :例如 query 函数开始执行此方法

afterRemote :例如 query 函数结束时执行此方法

由于篇幅过长在ANR分析(二)中分享
3.应用如何避免ANR
4.如何分析ANR问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值