Android 异常与性能优化
ANR(Application NotResponding)
如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发 “应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框,为用户提供强制退出应用的选项。
出现以下任何情况时,系统都会针对应用触发 ANR:
当 activity 位于前台时,应用在 5 秒钟内未响应输入事件或 BroadcastReceiver(如按键或屏幕轻触事件)。
虽然前台没有 activity,BroadcastReceiver 用了相当长的时间仍未执行完毕。
不同的组件发生 ANR 的时间不一样,Activity 是 5 秒,BroadCastReceiver 是 10 秒,Service 是 20 秒(均为前台)。
造成 ANR 的主要原因
主线程被 IO 操作(从 4.0 之后网络 IO 不允许在主线程中)阻塞。
主线程中存在耗时的计算
主线程中错误的操作,比如 Thread.wait 或者 Thread.sleep 等
Android 在主线程中的操作
Activity 的所有生命周期回调都是执行在主线程的
Service 默认是执行在主线程的
BroadcastReceiver 的 onReceive 回调是执行在主线程的
没有使用子线程的 looper 的 Handler 的 handleMessage,post (Runnable) 是执行在主线程的
AsyncTask 的回调中除了 doInBackground,其他都是执行在主线程
如何解决 ANR
使用 AsyncTask 处理耗时 IO 操作
使用 Thread 或者 HandlerThread 时,调用 Process.setThreadPriority (Process.THREAD_PRIORITY_BACKGROUND) 设置优先级,否则仍然会降低程序响应,因为默认 Thread 的优先级和主线程相同
使用 Handler 处理工作线程的耗时任务,而不是使用 Thread.wait () 或者 Thread.sleep () 来阻塞主线程。
Activity 的 onCreate 和 onResume 回调中尽量避免耗时的代码。 BroadcastReceiver 中 onReceive 代码也要尽量减少耗时,建议使用 IntentService 处理。
OOM
当前占用的内存加上我们申请的内存资源超过了 Dalvik 虚拟机的最大内存限制就会抛出的 Out of memory 异常。
OOM 的产生
已使用内存 + 新申请内存 > 可分配内存
OOM 几乎覆盖所有的内存区域,通常指堆内存
Native Heap 在物理内存不够时也会抛 OOM
解决 OOM
有关 bitmap
加载合适尺寸的图片
及时释放内存
图片压缩
inBitmap 属性
捕获异常
其他方法
listview:convertview/lru
避免在 onDraw 方法里面执行对象的创建
谨慎使用多进程
内存泄漏
检测工具:
Memory Profile
LeakCanary
MAT
内存泄漏 VS 内存溢出
内存溢出:超出了虚拟机分配的内存
内存泄漏:某个不再使用对象由于被其他实例引用
为什么会内存泄漏
该被回收的对象不能被回收
常见内存泄漏
单例
长生命周期对象持有了短生命周期的引用导致短生命周期对象需要被回收的时候无法回收
解决:需要使用 context 时使用
getApplicationContext()
Handler
非静态内部类,持有外部类 XXXActivity 的引用,延时发送消息导致
解决:
Activity 中避免使用 Handle 非静态内部类,声明为静态的
Handler 中通过
WeakReference
弱引用方式来引用 Activity在 Activity 销毁时移除 message:removeCallbacksAndMessages
线程引起
匿名内部类创建的 AsycTask、Runnable 会持有当前外部类 Activity 隐式的引用,Activity 销毁前 AsycTask、Runnable 内部耗时任务未完成导致 Activity 无法回收
解决:和 Handler 类似,使用静态内部类、销毁时取消任务
Webview
其他
非静态内部类创建静态实例引起
解决:把内部类修改为静态的
非静态匿名内部类引起
解决:将匿名内部类设置为静态的
注册 / 反注册未成对使用引起
注册广播接受器、EventBus 等,记得解绑
资源对象没有关闭引起
在这些资源不使用的时候,记得调用相应的类似 close()、destroy()、recycler()、release()等方法释放。
集合对象没有及时清理引起
通常会把一些对象装入到集合中,当不使用的时候一定要记得及时清理集合,让相关对象不再被引用
如何计算图片占用内存的大小
运行时获取 Bitmap 大小的方法
图片内存体积优化
跟文件存储格式无关
使用 inSampleSize 采样:大图 -> 小图
使用矩阵变换来放大图片:小图 -> 大图
使用 RGB_565 来加载不透明图片
使用 9-patch 图片做背景
不使用图片
优先使用 VectorDrawable
时间和技术允许的前提下使用代码编写动画