思考下面代碼html
1 public class SampleActivity extends Activity { 2 3 private final Handler mLeakyHandler = new Handler() { 4 @Override 5 public void handleMessage(Message msg) { 6 // ... 7 } 8 } 9 }
若是沒有仔細觀察,上面的代碼可能致使嚴重的內存泄露。Android Lint會給出下面的警告:android
In Android, Handler classes should be static or leaks might occur.框架
可是究竟是泄漏,如何發生的?讓咱們肯定問題的根源,先寫下咱們所知道的
一、當一個Android應用程序第一次啓動時,Android框架爲應用程序的主線程建立一個Looper對象。一個Looper實現了一個簡單的消息隊列,在一個循環中處理Message對象。全部主要的應用程序框架事件(如活動生命週期方法調用,單擊按鈕,等等)都包含在Message對象,它被添加到Looper的消息隊列而後一個個被處理。主線程的Looper在應用程序的整個生命週期中存在。
二、當一個Handle在主線程被實例化,它就被關聯到Looper的消息隊列。被髮送到消息隊列的消息會持有一個Handler的引用,以便Android框架能夠在Looper最終處理這個消息的時候,調用Handler#handleMessage(Message)。
三、在Java中,非靜態的內部類和匿名類會隱式地持有一個他們外部類的引用。靜態內部類則不會。ide
那麼,究竟是內存泄漏?好像很難懂,讓咱們如下面的代碼做爲一個例子oop
1 public class SampleActivity extends Activity { 2 3 private final Handler mLeakyHandler = new Handler() { 4 @Override 5 public void handleMessage(Message msg) { 6 // ... 7 } 8 } 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 14 // 延時10分鐘發送一個消息 15 mLeakyHandler.postDelayed(new Runnable() { 16 @Override 17 public void run() { } 18 }, 60 * 10 * 1000); 19 20 // 返回前一個Activity 21 finish(); 22 } 23 }
當這個Activity被finished後,延時發送的消息會繼續在主線程的消息隊列中存活10分鐘,直到他們被處理。這個消息持有這個Activity的Handler引用,這個Handler有隱式地持有他的外部類(在這個例子中是SampleActivity)。直到消息被處理前,這個引用都不會被釋放。所以Activity不會被垃圾回收機制回收,泄露他所持有的應用程序資源。注意,第15行的匿名Runnable類也同樣。匿名類的非靜態實例持有一個隱式的外部類引用,所以context將被泄露。post
爲了解決這個問題,Handler的子類應該定義在一個新文件中或使用靜態內部類。靜態內部類不會隱式持有外部類的引用。因此不會致使它的Activity泄露。若是你須要在Handle內部調用外部Activity的方法,那麼讓Handler持有一個Activity的弱引用(WeakReference)以便你不會意外致使context泄露。爲了解決咱們實例化匿名Runnable類可能致使的內存泄露,咱們將用一個靜態變量來引用他(由於匿名類的靜態實例不會隱式持有他們外部類的引用)。this
1 public class SampleActivity extends Activity { 2 /** 3 * 匿名類的靜態實例不會隱式持有他們外部類的引用 4 */ 5 private static final Runnable sRunnable = new Runnable() { 6 @Override 7 public void run() { 8 } 9 }; 10 11 private final MyHandler mHandler = new MyHandler(this); 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 17 // 延時10分鐘發送一個消息. 18 mHandler.postDelayed(sRunnable, 60 * 10 * 1000); 19 20 // 返回前一個Activity 21 finish(); 22 } 23 24 /** 25 * 靜態內部類的實例不會隱式持有他們外部類的引用。 26 */ 27 private static class MyHandler extends Handler { 28 private final WeakReference<SampleActivity> mActivity; 29 30 public MyHandler(SampleActivity activity) { 31 mActivity = new WeakReference<SampleActivity>(activity); 32 } 33 34 @Override 35 public void handleMessage(Message msg) { 36 SampleActivity activity = mActivity.get(); 37 38 if (activity != null) { 39 // ... 40 } 41 } 42 } 43 }
靜態和非靜態內部類的區別是比較難懂的,但每個Android開發人員都應該瞭解。開發中不能碰的雷區是什麼?不在一個Activity中使用非靜態內部類, 以防它的生命週期比Activity長。相反,儘可能使用持有Activity弱引用的靜態內部類。spa
譯文連接線程