Android崩溃优化学习笔记

Android中的两种崩溃分别是Java崩溃和Native崩溃。

  • Java崩溃:Java代码中出现未捕获异常,导致程序异常退出。
  • Native崩溃:Native代码中访问非法地址、地址对齐出现问题、程序主动abort。

难点在于Native崩溃的捕获,其流程如下:

  • 编译端:编译C/C++代码时,将带符号信息的文件保存下来。
  • 客户端:捕获崩溃时,尽可能收集有用信息写入日志文件,在合适的时机上传服务器。
  • 服务端:读取客户端上报的日志文件,寻找合适的符号文件,生成可读的C/C++调用栈。

如果涉及到Native开发,则可以使用Chromium的BreakPad来捕获崩溃,生成minidump文件,进行解析定位到问题源码处。

崩溃服务的选择: 腾讯的Bugly、阿里的啄木鸟平台。

关于如何衡量应用的稳定性,考虑到应用有如下这些异常退出的情况:

  1. 主动自杀,Process.killProcess、exit()等。
  2. 崩溃,Java、Native。
  3. 系统重启,通过比较应用开机运行时间是否比之前记录的值小。
  4. 被系统杀死,被low memory kill杀掉、从系统的任务管理器划掉等。
  5. 发生ANR。

检测异常退出的方法:
在应用启动的时候设置一个标志,若发生了异常退出,则更新这个标志,下次启动时检查这个标志即可知道上次是否发生了异常退出。每次启动都把flag置1,后面启动成功就把flag置0。如果下次启动发现flag是1,那就是启动异常。这样检测可反映ANR、low memory killer、被系统杀死、死机、断电等情况,不过从任务管理器划掉这种情况也会被统计进去。

用一个异常率的指标来衡量应用稳定性,定义如下:

UV 异常率 = 发生异常退出或崩溃的 UV / 登录 UV

核心是跟踪这个数据的变化。

崩溃时需要采集的一些有用信息:

  • 基本的崩溃信息

    • 进程名、线程名,前台还是后台线程,是否发生在UI线程。
    • 崩溃堆栈和类型,Java、Native、ANR,重点关注栈顶,发生在系统代码还是业务代码。
  • 系统信息

    • Logcat:应用、系统的运行日志。
    • 机型、系统、厂商、CPU、ABI、Linux 版本等。
    • 设备状态:是否 root、是否是模拟器。
获取logcat的方法:
通过hook liblog.so 中__android_log_buf_write 方法,将内容重定向到自己的buffer中。
优点:简单,兼容性相对还好。
缺点:要一直打开。
  • 内存信息

    • 系统剩余内存,读取文件 /proc/meminfo获取内存状态。
    • 应用使用内存,Java内存、RSS、PSS,得出应用本身占用内存大小和分布。PSS 和 RSS 通过 /proc/self/smap 计算,可以进一步得到例如 apk、dex、so 等更加详细的分类统计。
    • 虚拟内存,通过 /proc/self/status 得到,通过 /proc/self/maps 文件可以得到具体的分布情况。
  • 资源信息
    可能跟内存泄漏有关系。

    • 文件句柄,通过 /proc/self/limits 获得文件句柄限制,单个进程一般为1024,超过800则把所有fd以及对应文件名输出到日志,排查文件或线程泄漏。
    • 线程数,也是通过/proc/self/status得到,过多的线程会对虚拟内存和文件句柄带来压力,超过400个则要把所有线程id及线程名输出到日志中,排查线程相关问题。
    • JNI,通过DumpReferenceTable统计JNI引用表,分析JNI泄漏。
  • 应用信息

    • 崩溃场景,发生在哪个activity、fragment,对应的模块。
    • 关键操作路径,记录用户的关键操作路径。
    • 其它自定义信息,如运行时间、是否加载补丁等。

采集到有用信息后进行分析
分为三步

  1. 第一步:确定重点,找到日志中的关键信息,做一个大致判断

    • 确定严重程度,优先解决Top崩溃或对业务有重大影响的崩溃,如启动崩溃。

    • 崩溃基本信息,确定崩溃的类型和异常描述。如Java崩溃NullPointerException、OutOfMemoryError等关键信息;Native崩溃则观察signal、code、fault addr等内容,获取Java堆栈;ANR则先看主线程的堆栈,看是否是因为锁等待导致,接着看 ANR 日志中 iowait、CPU、GC、system server 等信息,进一步确定是 I/O 问题,或是 CPU 竞争问题,还是由于大量 GC 导致卡死。

    • Logcat,关注warning和error级别。ANR->“am_anr”、被系统杀死->"am_kill。

    • 各个资源情况,结合基本信息,看是跟内存信息有关或是跟资源信息有关。

  2. 第二步:查找共性,即查找这类崩溃的共性,如机型、系统版本等。

  3. 第三步:尝试复现,如根据收集到的用户操作路径来复现。

Native崩溃时获取Java堆栈的方法:
首先通过unwind拿到native堆栈,Java堆栈则通过hook ThreadList和Thread的函数,获得跟ANR一样的堆栈。为了稳定性,可以在fork子进程执行。
优点:信息很全,基本跟ANR的日志一样,有native线程状态,锁信息等等。
缺点:黑科技的兼容性问题,失败时可以用Thread.getAllStackTraces()兜底
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值