Android Handler leak 分析及解决办法,使用WeakReference

转载:http://my.oschina.net/dragonboyorg/blog/160986

In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread's MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well.To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

     在Android中,Handler类应该是静态的,否则,可能发生泄漏。在应用程序线程的MessageQueue中排队的Message对象还保留他们的目标Handler。如果Handler是一个内部类(注:无论是匿名还是非匿名,匿名是比较常见用法),它的外部类将被保留(至于为什么,请参考Java嵌套类相关说明)。为了避免泄漏外部类,声明一个Handler子类为静态内部类(注:这样就避免了Handler对象对外部类实例的自动引用),其内部持有一个对外部类对象的WeakReference。

     上面是HandlerLeak的详细解释,同时下划线部分也提供了解决方案的思路。我们需要再分析一下几个泄漏问题:(1)排队中的Message对象对Handler的持有导致泄漏;(2)Handler对象对外部类(如Activity或Service)实例的强引用持有。是由于这两个原因同时作用导致出现泄漏的可能。我们的解决方案可以从原因出发,清除这两个原因,就会比较完整的解决这个问题。

    方案:(1)针对第1个原因,在使用Handler的组件生命周期结束前清除掉MessageQueue中的发送给Handler的Message对象(例如在Activity或Service的onDestroy()中调用Handler的remove*方法);(2)针对第2个原因,Handler的实现类采用静态内部类的方式,避免对外部类的强引用,在其内部声明一个WeakReference引用到外部类的实例。

     关于Handler的remove*方法,这儿介绍一下(可以参考源码或文档)

removeCallbacks(Runnable r) ——清除r匹配上的Message。

removeCallbacks(Runnable r, Object token) ——清除r匹配且匹配token(Message.obj)的Message,token为空时,只匹配r。

removeCallbacksAndMessages(Object token) ——清除token匹配上的Message。

removeMessages(int what) ——按what来匹配

removeMessages(int what, Object object) ——按what来匹配

我们更多需要的是清除以该Handler为target的所有Message(包括Callback),那么调用如下方法即可

     handler.removeCallbacksAndMessages(null);

 

package com.example.weakreferencehandler;

import java.lang.ref.WeakReference;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class MainActivity extends Activity {

	private static TextView showTv;
	public MyHandler handler = new MyHandler(this);

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		showTv = (TextView) findViewById(R.id.showTv);
		handler.sendEmptyMessage(1);
	}

	@Override
	protected void onDestroy() {
		handler.removeCallbacksAndMessages(null);
		super.onDestroy();
	}

	private static class MyHandler extends Handler {
		WeakReference mActivity = null;

		MyHandler(MainActivity mActivity) {
			this.mActivity = new WeakReference(mActivity);
		}

		@Override
		public void handleMessage(Message msg) {
			MainActivity outer = mActivity.get();
			if (outer == null) {
				System.out.println("outer is null");
				return;
			}
			if (msg.what == 1) {
				showTv.setText("handler");
			}
		}
	}

}


 以下是一些关于WeakReference使用的零散解释,都是从其他帖子搜集来的:

 

主要是处理handler的静态内部类MyHandler的弱引用,不会阻碍Activity回收,弱引用就是不会影响计数器状态的引用。这意味着即使我们拥有对某个对象的弱引用也不会阻止它被清除

 

弱引用对象,它们并不禁止其指示对象变得可终结,并被终结,然后被回收。弱引用最常用于实现规范化的映射。

假定垃圾回收器确定在某一时间点上某个对象是弱可到达对象。这时,它将自动清除针对此对象的所有弱引用,以及通过强引用链和软引用,可以从其到达该对象的针对任何其他弱可到达对象的所有弱引用。同时它将声明所有以前的弱可到达对象为可终结的。在同一时间或晚些时候,它将那些已经向引用队列注册的新清除的弱引用加入队列。

 

WeakReference是什么:
 先不看官方doc,让我们举个例子:
 对象a非常的消耗内存,我有一个WeakReference对象(wra),并且和对象a关联:(wra & a are good friends)
 那么,在虚拟机看来是什么样子呢:wra对象不是个垃圾,但是和wra对象相关联的对象(对象a)被认为是垃圾
 是的,垃圾就是垃圾,但是:垃圾并不会立刻被清理
 也就意味着:你仍然可以使用对象a,如果它还没被清理的情况下
 如果对象a已经被清理呢:你必须重新构建对象a,再一次,和wra关联
 
那样做有什么样的好处:
 你将可以肆无忌惮的申请任意多个“非常消耗内存”的对象(前提是,让他们和WeakReference关联)
 使用这些对象前,先判定他们有没有被清理
     如果是,重新构建该对象(可能重新构建并不繁琐)
     如果不是,直接使用
 总结:WeakReference负责了:释放策略
 
与WeakReference类似的还有:SoftReference,大同小异

你可能感兴趣的:(Android Handler leak 分析及解决办法,使用WeakReference)