內部Handler類引起記憶體洩露

許佳佳233發表於2016-03-27

原文地址:http://blog.chengyunfeng.com/?p=468

如果您在Activity中定義了一個內部Handler類,如下程式碼:

 

public class MainActivity extends Activity {
 
    private  Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //TODO handle message...
        }
 
    };
 
    @TargetApi(11)
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendMessageDelayed(Message.obtain(), 60000);
 
        //just finish this activity
        finish();
    }
}


Read more: http://blog.chengyunfeng.com/?p=468#ixzz445wT2Gpw

 

 

然後執行Android Lint工具會有一個記憶體洩露警告:

This Handler class should be static or leaks might occur (com.example.ta.MainActivity.1)

Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak

In Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.

原因是:

  1. 當Android應用啟動的時候,會先建立一個應用主執行緒的Looper物件,Looper實現了一個簡單的訊息佇列,一個一個的處理裡面的Message物件。主執行緒Looper物件在整個應用生命週期中存在。
  2. 當在主執行緒中初始化Handler時,該Handler和Looper的訊息佇列關聯。傳送到訊息佇列的Message會引用傳送該訊息的Handler物件,這樣系統可以呼叫 Handler#handleMessage(Message) 來分發處理該訊息。
  3. 在Java中,非靜態(匿名)內部類會引用外部類物件。而靜態內部類不會引用外部類物件。
  4. 如果外部類是Activity,則會引起Activity洩露 。

當Activity finish後,延時訊息會繼續存在主執行緒訊息佇列中1分鐘,然後處理訊息。而該訊息引用了Activity的Handler物件,然後這個Handler又引用了這個Activity。這些引用物件會保持到該訊息被處理完,這樣就導致該Activity物件無法被回收,從而導致了上面說的 Activity洩露。

要修改該問題,只需要按照Lint提示的那樣,把Handler類定義為靜態即可,然後通過WeakReference 來保持外部的Activity物件。

 

 private Handler mHandler = new MyHandler(this);
    private static class MyHandler extends Handler{
        private final WeakReference<Activity> mActivity;
        public MyHandler(Activity activity) {
            mActivity = new WeakReference<Activity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            System.out.println(msg);
            if(mActivity.get() == null) {
                return;
            }
        }
    }


Read more: http://blog.chengyunfeng.com/?p=468#ixzz445wZB4mt

 

 

所以,當你在Activity中使用內部類的時候,需要時刻考慮您是否可以控制該內部類的生命週期,如果不可以,則最好定義為靜態內部類。

 


Read more: http://blog.chengyunfeng.com/?p=468#ixzz445vzsIoB

相關文章