Android - Handler内存泄漏与分析
一、Android Handler四件套:Looper + MessageQueue + Message + Handler
1、Android应用程序启动时,Framework会为应用程序的主线程创建一个Looper对象,该对象包含一个消息队列MessageQueue,并且循环处理队列中的消息Message;
2、这些消息包括大多数应用程序framework事件,如 Activity生命周期方法调用、Button点击等;
3、在主线程中实例化一个Handler对象,它就会自动与主线程Looper关联(发送的消息通过该Looper引用添加入消息队列中),同时消息Message都会拥有一个Handler的引用(当Looper处理该消息时,会据此回调handleMessage方法);
4、主线程Looper对象伴随应用程序的整个生命周期;
二、静态内部类/匿名类
1、非静态内部类和匿名类都会潜在的引用它们所属的外部类,但是,静态内部类却不会;
2、静态匿名类
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
三、对象引用类型
1、强引用 strong :Object object=new Object(). 当内存不足时,JVM宁愿抛出OOM内存溢出异常,也不会回收强引用对象来解决内存不足问题;
2、软引用 soft :只有内存达到某个阈值才进行回收,常用于缓存;
3、弱引用 weak :只要被GC线程扫描到就进行回收;
如果想避免OOM发生,则使用软引用对象,即当内存不足时进行回收;
如果想尽快回收某些占用内存较大的对象,可以使用弱引用;
四、Handler引发的内存泄漏
1、引发内存泄漏的不合格代码:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
当activity结束(finish)时, 延时消息需要10分钟后才可以得到处理, 由上文可知, 这条消息持有对handler的引用, handler又持有其外部类(在这里, 即SampleActivity)的潜在引用。
这条引用关系一直保持到消息得到处理, 从而阻止了SampleActivity被GC回收。
注意, 上面代码中的Runnable匿名类同样也持有其外部类的潜在引用。
2、解决方案:
1) 使用静态内部类与静态匿名类, 因为它们都不会持有其外部类的引用;
2) 若需要在Handler内部调用Activity, 可以使用弱引用方式;
3) Activity的OnDestroy中取消未执行的消息, 因为此时Activity处于不可见状态了, 继续修改视图操作已经没有意义;
3、改进后合格代码:
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
五、本文重点:
非静态内部类/匿名类会持有外部类的引用
静态内部类/匿名类不会持有外部类的引用
静态匿名类例子:
private static final Runnable sRunnable=new Runnable(){
@override
public void run(){/*...*/}
}
Handler引起的内存泄漏解决方法:
使用静态内部类/匿名类
使用weakReference引用Activity
Activity的onDestroy中取消所有未处理消息:mHandler.removeCallbacksAndMessages(null);
参考文章:
Android之Handler内存泄漏分析及解决
http://www.jianshu.com/p/cb9b4b71a820
http://blog.csdn.net/card361401376/article/details/51493352
在Activity中使用Thread导致的内存泄漏
https://github.com/hehonghui/android-tech-frontier/tree/master/issue-7/在Activity中使用Thread导致的内存泄漏
Android 背景作業處理
http://blog.kent-chiu.com/2012/03/19/background_processing.html
浙公网安备 33010602011771号