ANR
ANR 简介
ANR
全称 (Application Not responding)
:指的是应用程序未响应,Android
系统对于事件的处理需要在一定时间内完成,如果超过该时间没有得到响应,就会造成ANR。这时候会弹出一个弹窗来告诉用户,当前应用程序未响应,是选择等待还是Force Close
。
ANR 场景
-
InputDispatching Timeout
:5秒内无法响应屏幕触摸事件或键盘输入事件 -
BroadcastQueue Timeout
:在执行前台广播(BroadcastReceiver)
的onReceive()
函数时10秒没有处理完成,后台为60秒。 -
Service Timeout
:前台服务20秒内,后台服务在200秒内没有执行完毕。 -
ContentProvider Timeout
:ContentProvider
的publish
在10s内没进行完。
解决 ANR
避免在主线程中做耗时操作,可以通过以下方式: - 使用AsyncTask
处理耗时IO操作 - 使用Thread
或HandlerThread
提供优先级,可以调用 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。 使用Handler
处理工作线程结果,而不是使用Thread.wait()
或者Thread.sleep()
来阻塞主线程。 - Activity
的onCreate
和onResume
回调中尽量避免耗时的代码 - BroadcastReceiver
中onReceive
代码也要尽量减少耗时,建议使用IntentService
处理。
ANR 分析
- 查看
Log
,因为logcat
清晰地记录了ANR发生的时间,以及线程的tid。 - 通过查看
/data/anr/traces.txt
即可,最新的ANR信息在最开始部分。从stacktrace
中即可找到出问题的具体行数。需要注意的是,产生新的ANR,原来的traces.txt
文件会被覆盖。 - 使用DDMS——Update Threads工具,通过阅读Update Threads的输出结果来查找。
推荐任玉刚写的开发艺术探索,对个人进阶成高级工程师很有帮助
OOM
OOM 简介
OOM
指的是Out of Memory
(内存溢出),当前占用的内存加上我们申请的内存资源超过虚拟机所分配的最大内存限制,并且虚拟机没有足够的内存来为对象分配空间,此时垃圾回收器也已经没有空间可回收时,就会抛出这个error
(注:是error
,因为这个问题已经严重到不足以被应用处理)。
其他相关概念
- 内存泄漏:指程序分配出去的内存不再使用,无法进行回收
- 内存抖动:指程序短时间内大量创建对象,然后回收的现象
OOM 场景
-
Acitivity
没有对栈进行管理,如果开启过多,就容易造成内存溢出 - 加载大的图片或者同时数量过多的图片的时候
- 数据库或者资源没有关闭
- 静态成员变量持有类的应用
- 非静态内部类持有外部类的引用,使用非静态内部类创建静态变量
- 单例引起内存泄露
-
Handler
造成内存泄露 - 线程周期不可控
- 无线循环的属性动画引起内存泄露
-
listView
等getView
方法没有复用 - 递归次数过多,也会导致内存溢出
- 频繁的内存抖动,也会造成OOM异常的发生,大量小的对象被平凡的创建,导致内存碎片,从而当需要分配内存的时候,虽然总体上还有内存分配,但是由于这些内存不是连续的,导致无法分配,系统就直接返回OOM了
OOM 避免
减小对象的内存占用
减少新分配的对象占用内存的大小,使用更加轻量级的对象,这是首要做的事 1. 使用更加轻量级的数据结构,比如ArrayMap
或者SparseMap
而不是HashMap
等传统数据结构 2. 避免在Android
中使用Enum
枚举 3. 减少Bitmap
对象的内存占用,由于Bitmap
非常容易占用大量内存,所以必须减少它的内存占用非常重要,可以通过以下方式: - inSampleSize
:缩放比例,首先计算出一个合适的缩放比例,再把图片载入到内存中,避免大图的载入过多的占用内存 - decode format
:解码格式,不同的解码格式差异非常大,所以得从选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8
选择合适的格式 4. 使用更小的图片
内存对象的复用
- 复用系统自带的资源
-
ListView
与GridView
重要ConvertView
,并且在ListView
与GridView
等显示大量图片控件里,需要使用LRU
机制来缓存处理好Bitmap
- 避免在
OnDraw
方法里执行对象的创建,由于onDraw
方法会被频繁调用,如果在这里面进行对象创建,会迅速增加内存,频繁GC
,并且会产生内存抖动 - 如果代码中使用到大量的字符串拼接操作,使用
StingBuilder
来替代频繁的"+"操作
避免内存泄漏
内存泄漏也是造成OOM的一个重要因素。当对象泄漏时,会占用内存大小,并导致后面对象的内存分配不足,造成OOM。 - 避免内部类引用Activity
导致的内存泄漏 - Bitmap
对象要及时回收 - 一些资源不使用要及时关闭,比如:BroadcastReceiver,ContentObserver,FileObserver,Cursor,Callback,EventBus
等在Activity onDestroy
或者某类生命周期结束之后一定要 unregister
或者close
掉,否则这个 Activity 类会被系统强引用,不会被内存回收。值得注意的是,关闭的语句必须在finally
中进行关闭,否则有可能因为异常未关闭资源,致使Activity泄漏。 - 要注销监听器
其他一些方式
- 使用
static
要注意,因为static
生命周期与Application
一样长,使用不当会造成内存泄漏 - 谨慎使用多进程,因为多进程会将应用的部分组件运行在单独的进程中,扩大了内存占用,增加了内存大小,使用不当会造成OOM
- 优化布局层次,减少内存消耗
- 使用
ProGuard
来移除无用代码 - 谨慎使用第三方库