在android程序中,所有的输入(key和touch等)事件是由底层的InputDispatcher分发到上层的InputManagerService的,再通过InputManagerService内部的InputMonitor送入WindowManagerService的Policy(PhoneWindowManager)中。整个流程可以参考事件在native和jni中的流程和事件在java framework中的流程。而在事件分发的过程中,如果应用程序不能及时响应,就会产生ANR。
产生 ANR 需要满足以下条件,
这里使用的是号称Google亲儿子的Google Pixel xl(Android 8.0系统)做的测试,生成一个按钮跳转到ANRTestActivity,在后者的onCreate()中主线程休眠20秒:
@Overrideprotected 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:
可以看到logcat清晰地记录了ANR发生的时间,以及线程的tid和一句话概括原因:WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。 最后一句The application may be doing too much work on its main thread.告知可能在主线程做了太多的工作。
刚才的log有第二句Wrote stack traces to ‘/data/anr/traces.txt’,说明ANR异常已经输出到traces.txt文件,使用adb命令把这个文件从手机里导出来:
cd D:\Android\AndroidSdk\platform-tools
此外,除了Windows的cmd以外,还可以使用AndroidStudio的Terminal来输入adb命令。
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.myjavatestBuild fingerprint: 'google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keys'ABI: 'arm64'Build type: optimizedZygote loaded classes=4681 post zygote classes=106Intern table: 42675 strong; 137 weakJNI: 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可以看出相关问题:
特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。
通过JDK提供的命令可以帮助分析和调试Java应用,命令为:
jstack {pid}
其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程,比如
7266 Test7267 Jps
上面例子只是由于简单的主线程耗时操作造成的ANR,造成ANR的原因还有很多:
解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS
解决办法:文件读写或数据库操作放在子线程异步操作。
解决办法:AndroidManifest.xml文件中可以设置 android:largeHeap=“true”,以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。
各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。