Android Handler引發的內存泄漏

相信很多人都這樣用過Handler:

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        //更新UI
        //...
        super.handleMessage(msg);
    }
};
new Thread(new Runnable() {
    @Override
    public void run() {
        //耗時操作
        //...
        handler.sendEmptyMessage(0);
    }
}).start();

很常見的一段段碼,對沒錯,但是確實發生了內存泄漏。假如當在處理耗時操作的時候,可能是某段網絡請求,也有可能是很復雜的計算,這時用戶等的不耐煩了退出當前界面,由于異步操作,此時雖然界面銷毀了,但是任務還在繼續,由于線程中,引用這當前Activity中的對象handler,而handler對象又隱式的應用了當前的Activity,因此雖然界面銷毀了,但是Activity并不會被回收。當任務執行完后,調用了handler方法,方法中又調用了更新界面,就會出現空指針異常崩潰。這算是最好的情況了,更嚴重的是由于Activity無法被銷毀,那么就是占著那個地方不做事,這就是所謂的站著茅坑不拉屎。那么這塊資源便浪費了,當用戶來回反復切換的時候,資源占用越來越大,最終就算不空指針異常 也會出現OOM,這是由于內存泄漏導致的。

內存泄露的危害
只有一個,那就是虛擬機占用內存過高,導致OOM(內存溢出),程序出錯。對于Android應用來說,就是你的用戶打開一個Activity,使用完之后關閉它,內存泄露;又打開,又關閉,又泄露;幾次之后,程序占用內存超過系統限制。

解決方案

  1. 在關閉Activity的時候停掉你的后臺線程任務。線程停掉了,就相當于切斷了Handler和外部連接的線,Activity自然會在合適的時候被回收。
  2. 如果你的Handler是被delay的Message持有了引用,那么使用相應的Handler的removeCallbacks()方法,把消息對象從消息隊列移除就行了。
  3. 我們換一種寫法,將handler對象聲明成靜態內部類,靜態類不持有外部類的對象,所以你的Activity可以隨意被回收。
private TestHandler testHandler=new TestHandler();
static class TestHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
        //更新界面操作
        //...
        super.handleMessage(msg);
    }
}

很快你會發現編譯無法通過。因為上面說過了。靜態類是不持有外部類的對象的,因此如果你在里面寫了更新界面的操作當然會編譯無法通過。解決辦法當然有,就在靜態類中在增加一個外部類的弱引用。

static class MyHandler extends Handler{
    private WeakReference<SearchContantActivity> mContext;
    public MyHandler(SearchContantActivity activity){
        mContext=new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        if(msg.what==0){
            SearchContantActivity activity=mContext.get();
            if(activity!=null){
                activity.xxx
            }
        }
        super.handleMessage(msg);
    }
}

但是這種仍然會出現異常,當你的界面退出后,界面的控件都會被銷毀了,此時異步任務執行完,觸發了handler發送消息,這時handler仍然會受到,從弱引用中拿到的Activity還是存在的,這時候你在去更新界面,那么理所當然就發生了NullPointException.所以最好的辦法就是界面在正常發送handler,界面銷毀就不發送handler,徹底切斷它與Activity的聯系,那么就一勞永逸了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,560評論 25 708
  • Android 內存泄漏總結 內存管理的目的就是讓我們在開發中怎么有效的避免我們的應用出現內存泄漏的問題。內存泄漏...
    _痞子閱讀 1,657評論 0 8
  • 這幾天發生了很多事情,這些事情對我來說有強大的沖擊力,而來得有點突然,我有點蒙圈,但也想去看看這些事情發生...
    如水的日記閱讀 245評論 0 0
  • 如果沒有明天,我今天一定要和路過的每一個人,每一個小動物,每一株植物微笑; 如果沒有明天,我一定要好好感謝那些出現...
    心庭閱讀 438評論 0 1
  • 我是誰? 我是一位程序員,準確的說是前端工程師,略帶調侃的稱作“碼農”,專攻web,生活在上海這座擁擠的城市。在網...
    錯碼匠閱讀 284評論 0 0