恼人的ANR
早先年用Android的时候,就连很多知名的app也总是莫名其妙崩溃,好像手机快的时候会崩溃,手机卡的时候app会卡死。卡死的时候会弹出来一个框,询问是要结束app还是继续等待。这就是ANR(Application Not Responding)无疑了。
ANR一般有三种类型:
- KeyDispatchTimeout(5 seconds) — 按键或触摸事件在5秒内无响应
- BroadcastTimeout(10 seconds) — BroadcastReceiver在10秒内无法处理完成
- ServiceTimeout(20 seconds) — Service在20秒内无法处理完成
第一种类型很常见,就是你在UI线程里做了很多耗时的工作,导致UI阻塞住超过5秒,这个时候用户一定很气恼准备砸手机卖肾了,Android系统也会认为你的app做得实在是太烂了,赶紧提示用户把app结束掉,别让用户以为是安卓太烂了的缘故。
第二种也是一样,你刚刚接收到一个低电量的广播,要么赶紧弹一个框出来让用户注意电量(前台),要么赶紧开一个Service默默地杀掉耗电的app(后台),但是你若是在BroadcastReceiver里磨磨蹭蹭犹豫不决,Android系统也会认为你这个app简直是天秤座附体,赶紧回家纠结吧,别占着广播台了。
第三种比较少见,但也会出现,比如处理一些数据的时候算法不够优化,写大量文件啊,等等。
处理办法
- 1和3的处理办法就是:开线程、开线程、开线程,然后用上一课我们学到的Handler去异步处理UI。
- 2的话和上文提到的一样,要么转换成1的问题或者3的问题,不要停留在Broadcast里。
防患于未然
耗时的操作主要是I/O操作:
- 网络请求
当你准备进行网络操作的时候,想都不要想,一定要开线程。在Android2.x的时候还允许你在主线程里进行网络请求(只要不超过ANR的5秒限制),现在的Android4.x就不要想了,连ANR的机会都不会给你,直接报异常退出。
- 文件读写
当你用java的File操作的时候当然会警觉,会想到要开线程。但是有些坑你未必就能注意到:
- 数据库操作,尤其是可能会有大量批量的数据库操作的时候。
(我表示曾经被坑过,即便你是开了线程,在大量数据库操作时的超级慢也会让人受不了,你需要搜索一下“SQLite的事务处理”,症结在这儿。
ps:不只是SQLite,数据库在批量操作的时候都需要“事务处理” 。)
- 注意SharedPreferences,由于SharedPreferences封装的很好,所以很多时候往往会忘记这东西本质上是在读写xml文件啊!
虽然一般用这种方式存储是很少的数据量,但依然不可小觑。还有个小细节也要改变一下以往的习惯:
SharedPreferences sp=context.getSharedPreferences("xxx",context.MODE_PRIVATE); SharedPreferences.Editor editor=sp.edit(); editor.putInt("num",1); // editor.commit(); 没错,旧方法是使用commit(),它是直接操作文件的 editor.apply(); //推荐使用新方法apply(),它是异步的
- 计算密集型操作
没啥说的,异步,然后优化算法吧。如果计算结果不是急需要的话可以托管给服务端来处理嘛!
一些参考数据增加你的经验
- 100到200ms是用户能感知阻滞的时间阈值
- /data/anr/traces.txt里记录了你的应用程式发生过的ANR的一些信息
有兴趣可以动手试试,traces.txt能提供很多很多有用的数据
$chmod 777 /data/anr $rm /data/anr/traces.txt $ps $kill -3PID adbpull data/anr/traces.txt ./mytraces.txt
用这些命令可以有助于你清晰地查看traces.txt
参考资料:http://mzh3344258.blog.51cto.com/1823534/804237
总结
总而言之,ANR的存在就是在不断提醒你,优化,优化,再优化。优化效率,优化视觉,优化体验。
原文来自个人博客:【第三课】anr和oom-贪快和贪多的后果(上)
by:cyhhao http://blog.zhusun.in/cyhhao/