前言
本文对Android中常发生的ANR现象的成因原理及主要发生场景进行了详细介绍,举例了几种典型的ANR场景实例。总结提供了优化改善的若干解决思路。
值得Android开发人员收藏
简介
ANR全称:Application Not Responding,也就是应用程序无响应
针对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异常
ANR分析办法一:Log
刚才产生ANR后,看下Log:
可以看到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 SDK的platform-tools
目录,例如:
cd D:\Android\AndroidSdk\platform-tools
此外,除了Windows的cmd以外,还可以使用AndroidStudio的Terminal来输入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源码分析
crash监控方案
线程监控 - 死锁、存活周期与 CPU 占用率
启动速度与执行效率优化项目实战
Android卡顿检测及优化
- 卡顿
- 帧率
- 卡顿原因
- 卡顿检测
使用dumpsys gfxinfo
使用systrace
使用BlockCanary
内存优化
- Android内存优化工具
top
dumpsys meminfo
Memory Profiler
Leak Canary
MAT
内存问题高效分析方法 - Android内存泄漏分析及检测工具LeakCanary简介
- 安卓内存优化
「上图展示的只是一部分」
由于文章篇幅有限,后续还有耗电优化、网络传输与数据存储优化、apk大小优化、实战项目等知识要点
有需要的可在我的QQ技术交流群里可以自助拿走,如果在学习或工作中遇到了问题,群里会有一些大神帮忙解答,有时你闷头想一天,不如别人的三言两语就醍醐灌顶,群793544421
加入圈子,免费领取