記錄:關於匿名Handler內存泄露問題

RT:

在Inspect Code中發現了某個內存泄露的提示


提示在這個Activity類中可能存在內存泄露,代碼如下


常規的Handler寫法,此處看的有點蒙,爲什麼會有內存泄露,於是度娘查了一下。

當使用內部類(包括匿名類)來創建Handler的時候,Handler對象會隱式地持有一個外部類對象(通常是一個Activity)的引用。而Handler通常會伴隨着一個耗時的後臺線程(例如從網絡拉取圖片)一起出現,這個後臺線程在任務執行完畢(例如圖片下載完畢)之後,通過消息機制通知Handler,然後Handler把圖片更新到界面。然而,如果用戶在網絡請求過程中關閉了Activity,正常情況下,Activity不再被使用,它就有可能在GC檢查時被回收掉,但由於這時線程尚未執行完,而該線程持有Handler的引用,這個Handler又持有Activity的引用,就導致該Activity無法被回收(即內存泄露),直到網絡請求結束(例如圖片下載完畢)。另外,如果你執行了Handler的postDelayed()方法,該方法會將你的Handler裝入一個Message,並把這條Message推到MessageQueue中,那麼在你設定的delay到達之前,會有一條MessageQueue -> Message -> Handler -> Activity的鏈,導致Activity被持有引用而無法被回收

在查明原因後,有兩種解決方案,如下:

1、通過程序邏輯來進行保護,即removeCallbacks()方法去移除消息隊列中的消息。

2、將Handler聲明成靜態類,代碼如下:

private static class MyHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
    }
}
將Handler如此聲明後,會發現程序不能在這裏面操作Activity對象了,所以需要在Handler中創建一個Activity的弱引用(WeakReference),不明白的請自行百度WeakReference,此時代碼如下:

private static class MyHandler extends Handler{
    private final WeakReference mActivity;
    public MyHandler (AsynTaskListActivity activity){
        mActivity = new WeakReference(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        AsynTaskListActivity activity = mActivity.get();
        if (activity != null) {
            switch (msg.what){
                 code...
            }
        }
    }
}
如此大功告成!!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章