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會有以下提示:

(原發表在公司內部)

相關文章
相關標籤/搜索