ANR(Application Not Responding),应用程序无响应,会严重影响用户体验。作为测试开发人员更深入的理解 ANR 原理,可以更好的针对各类卡顿性能问题制定对应的监控策略。本文简单总结了 Android 系统的 ANR 监测与现有的监测方案的原理对比。
ANR 的本质是一个性能问题,即主线程中的耗时操作造成主线程堵塞,导致应用失去响应能力。常见的超时时限:
Service 与 Bradcast 只会打印 trace 信息,不会提示用户 ANR 弹窗,大部分可感知的 ANR 都是由于 InputEvent。
Android 应用程序是通过消息来驱动的,Android 某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关。Android 的 ANR 监测方案也是一样,大部分就是利用了 Android 的消息机制。
InputEvent 的 ANR 与上图有些许不同,是在 Native 监控,但同样会堵塞主线程的消息队列,后面会讲到一部分监测场景。
目前流行的 ANR 检测方案有开源的 BlockCanary 、ANR-WatchDog、SafeLooper, 还有根据谷歌原生系统接口监测的方案:FileObserver。下面就针对这四种方案根据场景解析对比。
BlockCanary 是国内开发者 markzhai 开发的一款非侵入式的轻量性能监控组件,在 Github 上有接近 4000 star。原理巧妙的利用了 Android 原生 Looper.loop 中的一个 log 打印逻辑。
这个 log 打印逻辑正是在 Message 消息分发前后,大部分的性能卡顿问题都是在这里发生的,监控这两个逻辑之间的时间差就可以得到当前主线程的卡顿状态,如果超时则获取 trace 信息并上报。具体实现:
优点
灵活配置可监控常见 APP 应用性能也可作为一部分场景的 ANR 监测,并且可以准确定位 ANR 和耗时调用栈。
缺点
但 BlockCanary 应用在 ANR 监控上有几个比较严重的问题
1、 谷歌已经明确标注 This must be in a local variable, in case a UI event sets the logger 这个 looger 对象是可以被更改的,已经有开发者遇到在使用 WebView 时 logger 被 set 为 Null 导致 BlockCanary 失效,只能让 BlockCanary 在 WebView 初始化之后调用 start。
2、 如果 dispatchMessage 执行的非常久是无法触发 BlockCanary 的逻辑。
3、 谷歌在 Looper 中还有一个标注
这里的 queue.next 是可能 block 的,场景就是文章开始提到的 InputEvent。此处 block 同样会触发 ANR,但 BlockCanary 同样无法适用的。一个例子可以验证下:
在 Activity 中重写 dispatchTouchEvent 和 dispatchKeyEvent,模拟耗时操作,弹出 ANR 告警,但 BlockCanary 没有任何反应,查看调用栈。
会看到 InputEvent 在 queue.next 中 block,不会继续执行 dispatchMessage,而是从 native 回调给 InputEventReceiver.dispatchInputEvent 处理分发,所以 BlockCanary 也就无法监控到这类 ANR。
4、 无法监控 CPU 资源紧张造成系统卡顿,无法响应的 ANR。
ANR-WatchDog 是参考 Android WatchDog 机制(com.android.server.WatchDog.java)起个单独线程向主线程发送一个变量 +1 操作,自我休眠自定义 ANR 的阈值,休眠过后判断变量是否 +1 完成,如果未完成则告警。
优点
1、 兼容性好,各个机型版本通用
2、 无需修改 APP 逻辑代码,非侵入式
3、 逻辑简单,性能影响不大
缺点
无法保证能捕捉所有 ANR,对阈值的设置直接影响捕获概率。如图:
如果对线程的堵塞大于 10s 则设置监控阈值 5s 能捕获所有 ANR,堵塞时间在 5s~10s,则可能出现无法捕获场景。
SafeLooper 是个比较新奇的思路,本身就是一个堵塞的消息,在自己内部进行消息的处理,通过反射接管主线程 Looper 的功能。
此方案使用反射进行 message 管理会有很大的性能损耗,但可以自由定制,这种 AOP 的思想是可以借鉴的。
有 ANR 的流程就可以知道/data/anr 文件夹的变化代表着 ANR 的发生,AMS 在 dumpStackTrace 方法中给了我们一些提示。
按照这个思路,当 ANR 发生的时候,我们是可以通过监听该文件的写入情况来判断是否发生了 ANR,看起来这是一个不错的时机。需要注意的是,所有应用发生 ANR 的时候都会进行回调,因此需要做一些过滤与判断,如包名、进程号等。ANR 生成的 trace 如图:
优点
1、基于原生接口调用,时机和内容准确。
2、无性能问题实现简单
缺点
最大的困难是兼容性问题,这个方案受限于 Android 系统的 SELinux 机制,5.0 以后基本已经使低权限应用无法监听到 trace 文件了,但是可以在开发内测阶段通过 root 手机修改 app 对应的 te 文件提权进行监控。Android 7.1.1 版本的测试截图
可以看到很多应用都尝试监听 ANR 文件,但是都被权限拒绝,属于不受信任应用。
SELinux(或 SEAndroid) 将 app 划分为主要三种类型 (根据 user 不同,也有其他的 domain 类型):
1、untrusted_app:第三方 app,没有 android 平台签名,没有 system 权限
2、platform_app:有 android 平台签名,没有 system 权限
3、system_app:有 android 平台签名和 system 权限
从上面划分,权限等级,理论上:untrusted_app < platform_app < system_app
本文汇总了目前主流的 ANR 监测方案的原理和实现,目前能了解到的方案并不太多,在 Goolge Play 上有 2.68% 实用率的 ACRA 库也只是推荐了 WatchDog 方式。建议 FileObserver 和 watchDog 组合使用,能覆盖绝大部分的机型和 ANR 异常。如果其他同学有更好的建议和方案欢迎交流,共同进步。
https://github.com/ACRA/acra
https://github.com/markzhai/AndroidPerformanceMonitor
https://github.com/SalomonBrys/ANR-WatchDog、
http://gityuan.com/2016/07/02/android-anr/
http://blog.csdn.net/zhudaozhuan/article/details/50964832