什麼是內部類?什麼是內存泄露?爲何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會有以下提示:
(原發表在公司內部)