Handler造成內(nèi)存泄露的分析和解決辦法

Android中使用Handler造成內(nèi)存泄露的分析和解決辦法


問題描述:This Handler class should be static or leaks might occur (anonymous android.os.Handler)(參考 https://my.oschina.net/liucundong/blog/294127

特性:當(dāng)Activity被finish()掉,Message 將存在于消息隊(duì)列中長達(dá)10分鐘的時間才會被執(zhí)行到。這個Message持有一個對Handler的引用,Handler也會持有一個對于外部類(SampleActivity)的隱式引用,這些引用在Message被執(zhí)行前將一直保持,這樣會保證Activity的上下文不被垃圾回收機(jī)制回收,同時也會泄露應(yīng)用程序的資源(views and resources)。 ADT20以后加入了一條新的檢查規(guī)則:確保類內(nèi)部的handler不含有對外部類的隱式引用 。

常見寫法

   private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                  
                    break;
                default:
                    break;
            }
        }
    };

上述寫法引起泄露原因是:

  • 當(dāng)Android應(yīng)用啟動的時候,會先創(chuàng)建一個應(yīng)用主線程的Looper對象,Looper實(shí)現(xiàn)了一個簡單的消息隊(duì)列,一個一個的處理里面的Message對象。主線程Looper對象在整個應(yīng)用生命周期中存在。

  • 當(dāng)在主線程中初始化Handler時,該Handler和Looper的消息隊(duì)列關(guān)聯(lián)。發(fā)送到消息隊(duì)列的Message會引用發(fā)送該消息的Handler對象,這樣系統(tǒng)可以調(diào)用 Handler#handleMessage(Message) 來分發(fā)處理該消息。

  • 在Java中,非靜態(tài)(匿名)內(nèi)部類會引用外部類對象。而靜態(tài)內(nèi)部類不會引用外部類對象。

-如果外部類是Activity,則會引起Activity泄露 。

  • 當(dāng)Activity finish后,延時消息會繼續(xù)存在主線程消息隊(duì)列中1分鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導(dǎo)致該Activity對象無法被回收,從而導(dǎo)致了上面說的 Activity泄露。

為解決這個問題,下面這段代碼中的Handler則是一個靜態(tài)匿名內(nèi)部類。靜態(tài)匿名內(nèi)部類不會持有一個對外部類的隱式引用,因此Activity將不會被泄露。如果你需要在Handler中調(diào)用外部Activity的方法,就讓Handler持有一個對Activity的WeakReference,這樣就不會泄露Activity的上下文了


  • 如下所示
private final MyHandler mHandler = new MyHandler(this);

static class MyHandler extends Handler {

        private final WeakReference<CashActivity> mActivity;

        public MyHandler(CashActivity activity) {
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            System.out.println(msg);
            if (mActivity.get() == null) {
                return;
            }
            CashActivity activity = mActivity.get();
            switch (msg.what) {
                case 0:
               activity.submit_but.setVisibility(View.VISIBLE);
                    break;
                default:
                    break;
            }
        }
    }
}

同時我們盡量要在當(dāng)前Activity的生命周期內(nèi)結(jié)束對所有回調(diào)函數(shù)和message的引用

   @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
    

至此一個可以內(nèi)存泄露的問題輕松處理,get!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容