首先还是先了解一下什么叫内存泄露
####内存泄漏
Java 使用有向图机制,通过GC自动检查内存中的对象(什么时候检查交给虚拟机决定),如果GC发现一个或一组对象为不可达状态,则将该对象从内存中回收。
也就是说,一个对象不被任何引用所指向,则该对象会被在GC发现的时候被回收;
还有一种情况是,如果一组对象中只包含互相的应用,而没有来自他们外部的应用,仍然属于不可达的范围,同样会被GC回收。
可以总结为两个词:
- 不可达
- 无根
就会被GC回收。
内存泄漏会发生什么?
Android中经常出现的一种现象:内存占用越来越大,App越用越卡,只有在强制关闭程序后才会有好转的迹象,这个就属于内存泄漏。而导致内存越来越大的原因有很多,其中最主要的原因之一,就是内存泄漏。
Handler 是怎么把内存泄漏的?
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
iv_test.setImageBitmap(mBitmap);
}
}
这是一段很简单的 handler 。但使用内部类(包括匿名类)来创建Handler 的时候,Handler对象会*隐式*地持有一个外部类对象的引用(一般是一个 Activity)。
而Handler的主要使用场景是处理一个耗时的任务,等待任务处理完成之后,通过Handler 的消息机制通知到 Activity,然后完成更新界面的工作。
但是,移动端的交互方式决定了何时关闭Activity是由用户决定的,如果在耗时任务完成之前,Activity就被关闭了。正常情况下,由于用户关闭了Activity,所以Activity申请的相关资源内存,应该要被释放掉。
但是,之前的 【学习笔记】Looper 与 Handler 的关系 学习到了一点,Handler在发送消息的时候,会把Message 排队到 一个 消息队列里面 (MessageQueue),等到任务完成后,再从消息队列里面取出来,通知handler,这里的关系是 引用持有关系可以表现为 MessageQueue -> Message -> Handler -> Activity,但任务没有完成的情况下,Activity理应被回收的业务逻辑下,由于Activity的引用被持有,导致GC不会去进行回收操作,这个就形成了内存泄漏。
内存泄漏有什么危害
导致虚拟机占用内存过高,导致OOM,程序崩溃。对于Android来说,目前这种Android手机越用越卡的第一印象很大程度上与应用的质量有很大的关系,应用没有很好地解决性能问题(内存泄漏只是其中一种情况)。用户打开一个Activity,关闭的时候并没有把之前向系统申请的内存回收掉,反复操作几次之后,就会出现内存超过系统限制的问题而被强制关闭,用户体验就不好了。
Handler导致内存泄漏的解决办法
- 通过程序逻辑进行保护
- 在Activity被关闭的时候,进行耗时任务的停止工作,任务被停止了,Activity也就没有被引用了。自然也就回收了。
- 在Activity被关闭的时候,在消息队列里面把Message消息(包含 Handler引用)remove掉,使用 handler.removeCallback() 方法,同样达到去除引用的效果。
总的来说就是 在Activity关闭的时候做好 耗时任务的处理,不管是停止任务,还是removeCallback ,这个习惯总是好的。
- 针对使用内部类的解决办法
静态类不持有外部类的对象,所以Activity可以被随时回收。
static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
iv_test.setImageBitmap(mBitmap);
}
}
由于Handler 不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以使用一个Activity的弱引用(WeakReference)方式解决。
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
iv_test.setImageBitmap(mBitmap);
}
}
}
到这里就完成。
延伸
什么叫 弱引用