• @Android ANR 监测方案解析


    ANR的触发条件

    ANR的本质是一个性能问题,即主线程中的耗时操作造成主线程堵塞,导致应用失去响应能力。常见的超时时限:

     

    Service与Bradcast只会打印trace信息,不会提示用户ANR弹窗,大部分可感知的ANR都是由于InputEvent。

    Android对ANR的监控机制

    Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关。Android的ANR监测方案也是一样,大部分就是利用了Android的消息机制。

     

    InputEvent的ANR与上图有些许不同,是在Native监控,但同样会堵塞主线程的消息队列,后面会讲到一部分监测场景。

    应用ANR检测方案

    目前流行的ANR检测方案有开源的BlockCanary 、ANR-WatchDog、SafeLooper, 还有根据谷歌原生系统接口监测的方案:FileObserver。下面就针对这四种方案根据场景解析对比。

    BlockCanary

    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

    ANR-WatchDog是参考Android WatchDog机制(com.android.server.WatchDog.java)起个单独线程向主线程发送一个变量+1操作,自我休眠自定义ANR的阈值,休眠过后判断变量是否+1完成,如果未完成则告警。

     

     
    • 优点
      1、 兼容性好,各个机型版本通用
      2、 无需修改APP逻辑代码,非侵入式
      3、 逻辑简单,性能影响不大

    • 缺点
      无法保证能捕捉所有ANR,对阈值的设置直接影响捕获概率。如图:

       

      如果对线程的堵塞大于10s则设置监控阈值5s能捕获所有ANR,堵塞时间在5s~10s,则可能出现无法捕获场景。

    SafeLooper

    SafeLooper是个比较新奇的思路,本身就是一个堵塞的消息,在自己内部进行消息的处理,通过反射接管主线程Looper的功能。

     

    此方案使用反射进行message管理会有很大的性能损耗,但可以自由定制,这种AOP的思想是可以借鉴的。

    FileObserver

    有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异常。如果其他同学有更好的建议和方案欢迎交流,共同进步。



    作者:奶盖ww
    链接:https://www.jianshu.com/p/473abd102604
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Linux统计文件夹下所有文件的数量
    Linux查看文件最后几行的命令
    linux export将PATH环境变量误删了的解决办法
    laravel提示Mcrypt PHP extension required
    php(cli模式)执行文件传递参数
    shell判断文件是否存在,不存在则创建
    php获取Linux网卡信息
    使用iptraf,ifstat查看网络流量
    作用域
    头文件,库文件,重复包含
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/14230278.html
Copyright © 2020-2023  润新知