翻譯自http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
在主線程中使用Handler對象,比如下面的代碼
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } }
這段代碼會產生隱秘的內存溢出的錯誤,Android Lint會給出羡慕的警告:
The handler class should be static or leaks might occur.
但是究竟哪一部分內存會在什么情況下溢出呢?
要解決這一問題需要了解一下安卓系統背景知識:
1. 當一個Android應用啟動的時候,Android系統為這個應用的主線程創建一個Looper對象。Looper對象實現了簡單的消息隊列(message queue),依次處理循環(Looper)中的消息。所有的主要應用事件(比如Activity的生命周期,按鈕的點擊等)都被包裹為一個消息(Message)實體,被添加到Looper的消息隊列中依次處理。主線程的Looper在應用的整個生命周期中都存在。
2.當一個Handler對象在主線程中初始化時,它被關聯到Looper的消息隊列中。在Handler的sendMessage()方法被調用的時候,一個Message對象會被發往Looper的消息隊列中,被發送到消息隊列中的Message將會持有Handler的引用,然后系統才能在Looper處理Message時調用Handler對象的handleMessage(Message)方法。
3.在Java中,非靜態(non-static)內部和匿名類將會持有外部類的引用。相反,靜態的內部類不會持有外部類的引用。
更多Looper,Handler相關的知識可以在文后鏈接的博客中找到。
了解了這些背景之后我們來看一下內存在什么情況下溢出,首先看下面一段代碼:
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
當Activity結束的時候,被延遲的Message會繼續存活在主線程中10分鍾,直到它被處理了。這個Message持有Activity的Handler對象的引用,同時Handler是Activity的非靜態(non-static)匿名內部類,所以Handler持有外部類也就是Activity的引用。這樣的引用關系會阻止Activity被Java GC回收,釋放系統持有的資源,直到Message被處理了。另外楊的引用對於匿名Runnable()對象也存在。
解決這個問題的方法可以有:在一個新的文件中繼承Handler類,或者使用一個靜態內部類。靜態內部類不會持有外部類的引用,所以Activity不會被泄露。如果需要在Handler中調用外部類(此處為Activiity)的方法,可以使Handler擁有Activity的弱引用(WeakReference),這樣就不會意外地泄露Activity。
為了解決實例化內部匿名Runnable類時導致的內存泄漏的問題,我們可以使內部匿名Runnable類變為外部類的靜態對象。
public class SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
在Activity中實例化內部類時,如果內部類可以在Activity的生命周期之外繼續存活於哦,那么這樣的內部類不能為非靜態的。這種情況應該使用靜態內部來並且持有外部類對象的弱引用。
相關鏈接:
http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html