Handler內(nèi)存泄露原理及解決方法

前言

因為Android采取了單線程UI模型,開發(fā)者無法在子線程中更新UI,為此Android為我們提供了Handler這個工具,可以開發(fā)者切換到主線程更新UI。

示例

首先看一段示例代碼

public class LeakCanaryActivity extends AppCompatActivity

    private  Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };

        Message message = Message.obtain();
        message.what = 1;
        mHandler.sendMessageDelayed(message,10*60*1000);
    }

}

這段代碼的邏輯很簡單,mHandler延時了10分鐘發(fā)送消息,類似的代碼在我們的項目中也經(jīng)常出現(xiàn),但是這樣的代碼會出現(xiàn)一個問題。

問題

我們在項目中集成 Square 的開源庫 LeakCanary,有關這個庫的介紹及使用請看:Github.LeakCanary。

我們首先打開 LeakCanaryActivity ,然后按返回鍵將這個Activity finish 掉。等待幾秒屏幕上會彈出提醒和通知,這說明此時發(fā)生了內(nèi)存泄露的現(xiàn)象。

原因

究竟是什么時候發(fā)生了內(nèi)存泄露的問題呢?

我們知道在Java中,非靜態(tài)內(nèi)部類會隱性地持有外部類的引用,二靜態(tài)內(nèi)部類則不會。在上面的代碼中,Message在消息隊列中延時了10分鐘,然后才處理該消息。而這個消息引用了Handler對象,Handler對象又隱性地持有了Activity的對象,當發(fā)生GC是以為 message – handler – acitivity 的引用鏈導致Activity無法被回收,所以發(fā)生了內(nèi)存泄露的問題。

危害

眾所周知,內(nèi)存泄露在 Android 開發(fā)中是一個比較嚴重的問題,系統(tǒng)給每一個應用分配的內(nèi)存是固定的,一旦發(fā)生了內(nèi)存泄露,就會導致該應用可用內(nèi)存越來越小,嚴重時會發(fā)生 OOM 導致 Force Close。

解決

這個問題該如何解決呢?

  1. 使用弱引用

    首先我們需要理解一下相關概念:

    • 強引用:強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內(nèi)存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內(nèi)存不足的問題。
    • 軟應用:如果一個對象只具有軟引用,則內(nèi)存空間足夠,垃圾回收器就不會回收它;如果內(nèi)存空間不足了,就會回收這些對象的內(nèi)存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現(xiàn)內(nèi)存敏感的高速緩存。
    • 弱引用:弱引用與軟引用的區(qū)別在于:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。不過,由于垃圾回收器是一個優(yōu)先級很低的線程,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象。

    用更直白的語言描述就是,java對于 強引用 的對象,就絕不收回,對于 軟引用 的對象,是能不收回就不收回,這里的能不收回就是指內(nèi)存足夠的情況,對于 弱引用 的對象,是發(fā)現(xiàn)就收回,但是一般情況下不會發(fā)現(xiàn)。

    很顯然,出現(xiàn)內(nèi)存泄露問提的原因,就是 Handler 對 Activity 是強引用,導致 GC 在回收 Activity 時無法回收。為了解決這個問題,我們可以把 Handler 對 Activity 弱引用,這樣 GC 就能把 Activity 及時的回收,從而杜絕了內(nèi)存泄露的問題。

    public class NoLeakActivity extends AppCompatActivity {
    
        private NoLeakHandler mHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            mHandler = new NoLeakHandler(this);
    
            Message message = Message.obtain();
    
            mHandler.sendMessageDelayed(message,10*60*1000);
        }
    
        private static class NoLeakHandler extends Handler{
            private WeakReference<NoLeakActivity> mActivity;
    
            public NoLeakHandler(NoLeakActivity activity){
                mActivity = new WeakReference<>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        }
    }
    
    

    運行項目,并沒有發(fā)生內(nèi)存泄露的問題。

  2. 及時清除消息

    在原因中我們說到,正是因為被延時處理的 message 持有 Handler 的引用,Handler 持有對 Activity 的引用,形成了message – handler – activity 這樣一條引用鏈,導致 Activity 的泄露。因此我們可以嘗試在當前界面結(jié)束時將消息隊列中未被處理的消息清除,從源頭上解除了這條引用鏈,從而使 Activity 能被及時的回收。

    public class LeakCanaryActivity extends AppCompatActivity {
    
        private  Handler mHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
    
                }
            };
    
            Message message = Message.obtain();
            message.what = 1;
            mHandler.sendMessageDelayed(message,10*60*1000);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacksAndMessages(null);
        }
    }
    
    

    運行項目,也沒有發(fā)生內(nèi)存泄露的問題。

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

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

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