❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

前言

本文对Android中常发生的ANR现象的成因原理及主要发生场景进行了详细介绍,举例了几种典型的ANR场景实例。总结提供了优化改善的若干解决思路。
值得Android开发人员收藏

简介

ANR全称:Application Not Responding,也就是应用程序无响应
针对Android中常发生的ANR现象,要解决此问题,当真要从其原理开始分析,如下图所示

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

原因

Android系统中,ActivityManagerService(简称AMS)WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。其原因无非是以下四点:

  • InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
  • BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
  • Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
  • ContentProvider Timeout :ContentProvider的publish在10s内没进行完。

ANR分析办法

ANR重现

这里使用的是号称Google亲儿子的Google Pixel xl(Android 8.0系统)做的测试,生成一个按钮跳转到ANRTestActivity,在后者的onCreate()中主线程休眠20秒:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_anr_test);
    // 这是Android提供线程休眠函数,与Thread.sleep()最大的区别是
    // 该使用该函数不会抛出InterruptedException异常。
    SystemClock.sleep(20 * 1000);
}

在进入ANRTestActivity后黑屏一段时间,大概有七八秒,终于弹出了ANR异常

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

ANR分析办法一:Log

刚才产生ANR后,看下Log:

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

可以看到logcat清晰地记录了ANR发生的时间,以及线程的tid和一句话概括原因:
WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。
最后一句The application may be doing too much work on its main thread.
告知可能在主线程做了太多的工作。

ANR分析办法二:traces.txt

刚才的log有第二句Wrote stack traces to '/data/anr/traces.txt',说明ANR异常已经输出到traces.txt文件,使用adb命令把这个文件从手机里导出来:
1、cd到adb.exe所在的目录,也就是Android SDKplatform-tools目录,例如:

cd D:\Android\AndroidSdk\platform-tools

此外,除了Windows的cmd以外,还可以使用AndroidStudioTerminal来输入adb命令。
2、到指定目录后执行以下adb命令导出traces.txt文件:

adb pull /data/anr/traces.txt

traces.txt默认会被导出到Android SDK\platform-tools目录。一般来说traces.txt文件记录的东西会比较多,分析的时候需要有针对性地去找相关记录。

----- pid 23346 at 2017-11-07 11:33:57 -----  ----> 进程id和ANR产生时间
Cmd line: com.sky.myjavatest
Build fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=4681 post zygote classes=106
Intern table: 42675 strong; 137 weak
JNI: CheckJNI is on; globals=526 (plus 22 weak)
Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so 
/system/lib64/libjavacrypto.so
/system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libsoundpool.so
/system/lib64/libwebviewchromium_loader.so libjavacore.so libopenjdk.so (9)
Heap: 22% free, 1478KB/1896KB; 21881 objects    ----> 内存使用情况

...

"main" prio=5 tid=1 Sleeping    ----> 原因为Sleeping
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x733d0670 self=0x74a4abea00
  | sysTid=23346 nice=-10 cgrp=default sched=0/0 handle=0x74a91ab9b0
  | state=S schedstat=( 391462128 82838177 354 ) utm=33 stm=4 core=3 HZ=100
  | stack=0x7fe6fac000-0x7fe6fae000 stackSize=8MB
  | held mutexes=
  at java.lang.Thread.sleep(Native method)
  - sleeping on <0x053fd2c2> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:373)
  - locked <0x053fd2c2> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:314)
  at android.os.SystemClock.sleep(SystemClock.java:122)
  at com.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20) ----> 产生ANR的包名以及具体行数
  at android.app.Activity.performCreate(Activity.java:6975)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
  at android.app.ActivityThread.-wrap11(ActivityThread.java:-1)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
  at android.os.Handler.dispatchMessage(Handler.java:105)
  at android.os.Looper.loop(Looper.java:164)
  at android.app.ActivityThread.main(ActivityThread.java:6541)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

在文件中使用 ctrl + F 查找包名可以快速定位相关代码。
通过上方log可以看出相关问题:

  • 进程id和包名:pid 23346 com.sky.myjavatest
  • 造成ANR的原因:Sleeping
  • 造成ANR的具体行数:ANRTestActivity.java:20类的第20行

特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。

ANR分析办法三:Java线程调用分析

通过JDK提供的命令可以帮助分析和调试Java应用,命令为:

jstack {pid}

其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程,比如

7266 Test
7267 Jps

ANR分析办法四:DDMS分析ANR问题

  • 使用DDMS——Update Threads工具
  • 阅读Update Threads的输出

造成ANR的原因及解决办法

上面例子只是由于简单的主线程耗时操作造成的ANR,造成ANR的原因还有很多:

  • 主线程阻塞或主线程数据读取

解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS

  • CPU满负荷,I/O阻塞

解决办法:文件读写或数据库操作放在子线程异步操作。

  • 内存不足

解决办法:AndroidManifest.xml文件<applicatiion>中可以设置 android:largeHeap="true",以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。

  • 各大组件ANR

各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务

本章节暂且结束,后续预告请往下看,如有需要后续文章资料,文末有领取方式

ANR源码分析

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

crash监控方案

线程监控 - 死锁、存活周期与 CPU 占用率

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

启动速度与执行效率优化项目实战

Android卡顿检测及优化

  • 卡顿
  • 帧率
  • 卡顿原因
  • 卡顿检测
    使用dumpsys gfxinfo
    使用systrace
    使用BlockCanary

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

内存优化

  • Android内存优化工具
    top
    dumpsys meminfo
    Memory Profiler
    Leak Canary
    MAT
    内存问题高效分析方法
  • Android内存泄漏分析及检测工具LeakCanary简介
  • 安卓内存优化

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

❤️烦恼?头疼?不知所措?Android的ANR问题,一剂药到病除❤️【建议收藏】

「上图展示的只是一部分」
由于文章篇幅有限,后续还有耗电优化、网络传输与数据存储优化、apk大小优化、实战项目等知识要点
有需要的可在我的QQ技术交流群里可以自助拿走,如果在学习或工作中遇到了问题,群里会有一些大神帮忙解答,有时你闷头想一天,不如别人的三言两语就醍醐灌顶,群793544421
加入圈子,免费领取

上一篇:Android 非UI线程不能更新UI


下一篇:Android开发涉及到的AMS类和ActivityThread类源码解读