我们知道,在主线程进行耗时任务会造成 ANR(Application Not Responding),其原因可能有:
- 5 秒内无法响应对输入事件(例如硬件点击或者屏幕触摸事件)
- BroadReceiver 不能够在 10 秒内执行完接收到的任务
- Service 在特定的时间内无法处理完成
有经验的同学都知道,网络操作、数据库读写、文件读写、大量计算等等这些耗时任务都应该做成异步的。但是,出于种种意外我们还是可能会碰到 ANR。
那我们用什么办法去应对呢?
StrictMode
StrictMode,即严格模式,可以根据我们设定的策略来检测代码中不规范的地方并给出提示,log、dialog 或者直接崩溃。具体的策略分两类,TreadPolicy and VmPolicy。
Thread Policy:
- Custom Slow Calls,耗时方法
- Disk Reads & Writes,磁盘读写
- Network,网络操作
- Resource Mismatches,资源不匹配
VM Policy:
- Activity Leaks,
- Class Instance Limit,类的对象数量上限
- Leaked Sql Lite Objects,SQL 对象泄露
- Leaked Closable Objects
更多的策略及其设置方法可以在 android.os.StrictMode
类中查看。我在 Application.onCreate() 中调用了以下方法,需要注意的是 StrictMode 最低支持 API 9.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private void toggleStrictMode(boolean isDebug) { if (!isDebug) { return; } StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy .Builder() .detectAll() .penaltyLog() .build(); StrictMode.setThreadPolicy(threadPolicy); StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy .Builder() .detectAll() .penaltyLog() .build(); StrictMode.setVmPolicy(vmPolicy); }
|
Analyzing ANR Trace File
事实上,发生 ANR 后,系统中会生成一个文件:/data/anr/traces.txt
,这个文件记录了 ANR 的一些信息,我们可以把它拿出来分析,命令如下:
1
| adb pull /data/anr/traces.txt ~/Downloads/traces.txt
|
在这个文件靠前的位置会保留最后一次发生 ANR 时被调用的方法栈,下面的 trace 信息显示了我的应用产生 ANR 的地方是调用了 com.iamwent.gank.ui.daily.DailyActivity.chooseOneDay
这个方法,里面使用了 Thread.sleep
。你不妨自己手动制造一个 ANR 然后看看这个文件的内容 ^_^
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| "main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 obj=0x76cf0fb8 self=0x7fb3874a00 | sysTid=12199 nice=0 cgrp=default sched=0/0 handle=0x7fb78c4fc8 | state=S schedstat=( 0 0 0 ) utm=62 stm=20 core=7 HZ=100 | stack=0x7fc7eac000-0x7fc7eae000 stackSize=8MB | held mutexes= at java.lang.Thread.sleep!(Native method) - sleeping on <0x056f8f79> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:1031) - locked <0x056f8f79> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:985) at com.iamwent.gank.ui.daily.DailyActivity.chooseOneDay(DailyActivity.java:177) at com.iamwent.gank.ui.daily.DailyActivity.onOptionsItemSelected(DailyActivity.java:157) at android.app.Activity.onMenuItemSelected(Activity.java:3204) at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:408) at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:195) at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:113) at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:113) at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:69)
|
More
还有一点,我们可以在开发者选项中打开 显示全部 ANR,可以显示所有后台应用 ANR 的对话框。不同的手机改选项的位置和名称可能略有不同。
我们避免 ANR 某种程度上也是为了提高应用的流畅度。在官方文档中提及,用户能够察觉到卡顿的上限一般是 100ms - 200ms,这也就是说,我们对 UI 的响应时间不要超过这个范围,这也就要求我们的方法(在主线程)的执行时间不要超过这个间隔,这个就是我们要研究的另一个问题了。
Reading