Android中的内部类引发的内存泄露

引子

什么是内部类?什么是内存泄露?为何Android的内部类容易引发内存泄露?如何解决?ide

什么是内部类?函数

什么是内部类?什么又是外部类、匿名类、局部类、顶层类、嵌套类?你们能够参考我这篇文章 ,再查查一些资料,先弄清楚什么是内部类和内部类的特性再向下看。this

常常会碰见Android程序中这样使用handler:spa

public class SomeActivity {
	// ......
	private Handler mHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			switch(msg.what) {
			case 0:
				// do something
				break;
			case 1:
				// do something
				break;
			default:
				break;
			}
		}
	};
	private void someMethod () {
		mHandler.sendEmptyMessage(0);
	}
}

上述代码中,mHandler字段指向一个匿名Handler类。匿名类是内部类吗?匿名类会持有外部类的对象吗? 答案是:匿名类是内部类,可是是特殊的内部类,若是把匿名类放到一个static方法中,它是不会持有外部类实例的。而在上面的代码中,这个mHandler会持有外部类(SomeActivity)实例的引用,由于它处于一个对象的上下文中,而不是类型上下文中。对象

什么是”持有外部类实例的引用“?你能够这么理解:队列

public class InnerClass {
    private OuterClass outer;
    public InnerClass(OuterClass outer) {
        this.outer = outer;
    }
}

就是说,建立InnerClass对象的时,必须传递进去一个OuterClass对象,赋值给InnerClass的一个字段outer,该字段是OuterClass对象的引用。回忆一下GC原理,若是InnerClass对象没有被标记为垃圾对象,那么outer指向的OuterClass对象会可能被标记为垃圾对象吗?答案是:InnerClass对象与GC Root有引用路径,InnerClass对象又引用了OuterClass对象,那么OuterClass对象到GC Root也是有引用路径的,因此,OuterClass不多是垃圾对象。内存

为何发生内存泄露?rem

由上文能够看出:当mHandler没有被回收时,其外围Activity对象不能被回收。当Activity被用户关闭(finish),而此时mHandler还未被回收,那么Activity对象就不会被回收,形成Activity内存泄露。get

问题的关键转入到了这个问题:为何Activity被finish了,mHandler还不能被回收?源码

发送消息时,咱们使用了这个函数:mHandler.sendEmptyMessage(0)函数。经过查看源码追踪调用关系,发现走到了:

Message对象有个target字段,该字段是Handler类型,引用了当前Handler对象。一句话就是:你经过Handler发往消息队列的Message对象持有了Handler对象的引用。假如Message对象一直在消息队列中未被处理释放掉,你的Handler对象就不会被释放,进而你的Activity也不会被释放。

这种现象很常见,当消息队列中含有大量的Message等待处理,你发的Message须要等几秒才能被处理,而此时你关闭Activity,就会引发内存泄露。若是你常常send一些delay的消息,即便消息队列不繁忙,在delay到达以前关闭Activity也会形成内存泄露。

有什么解决方案?

方案#1:在关闭Activity时(finish/onStop等函数中),取消还在排队的Message:mHandler.removeCallbacksAndMessages(null);

方案#2:使用WeakReference截断StrongReference。问题的症结既然是内部类持有外部类对象的引用,那我不用内部类就好了,直接使用静态成员类。但mHandler又须要与Activity对象交互,那就来个WeakReference,指向外部Activity对象。

public class SomeActivity {
	private Handler mHandler = new MyHandler(this);
	private static class MyHandler extends Handler {
		private WeakReference<SomeActivity> ref;
		public MyHandler(SomeActivity activity) {
			if (activity != null) {
				ref = new WeakReference<SomeActivity>(activity);
			}
		}
		@Override
		public void handleMessage(Message msg) {
			if (ref == null) {
				return;
			}
			SomeActivity v = ref.get();
			if (v == null) {
				return;
			}
			// handle message
		}
	}
}

当Activity想关闭销毁时,mHandler对它的弱引用没有影响,该销毁销毁;当mHandler经过WeakReference拿不到Activity对象时,说明Activity已经销毁了,就不用处理了,至关于丢弃了消息。

另外,当你使用Handler有内存泄露时候,Android Studio的Lint会有以下提示:

(原发表在公司内部)

相关文章
相关标签/搜索