詳解Android Handler 機制 (三)內存泄漏

ps:看本文以前最好先了解一下Handler源碼java

經常使用寫法

咱們通常使用Handler使用匿名內部類的寫法,也就是:android

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendEmptyMessage(MainActivity.HANDLER_MAIN_CODE);
            }
        });
複製代碼

這時咱們就會發現,匿名內部類的地方會報黃: image.png *這裏提示的大意是:這個Handler類應該被設置爲靜態不然可能發生內存泄漏。*wtf???爲何會發生內存泄漏。內存泄漏究竟是一個什麼東西。web

內存泄漏是個什麼東西

這個嘛,詳細內容你們能夠閱讀《深刻理解Java虛擬機》,前段時間剛出第三版,是紫色的,相比第二版加了不少內容。 簡單來講就是,當堆中分配的一塊內存區域使用完畢,以後不會在被使用到,應該要被回收時,若是還存在一個強引用引用着這塊內存區域,那麼這塊區域就沒法被回收。此時就會發生內存泄漏。markdown

使用內部類Handler內存泄漏的緣由

那爲何說使用匿名內部類來使用Handler 在handler.sengMessage時,Message擁有了Handler的引用,而內部類Handler隱式的擁有外部類(Activity)的引用。一個線程中有一個Looper,Looper中有惟一一個MessageQueue,而Message在MessageQueue中,也就是產生了下面這條引用鏈: Handler引用鏈app

若是此時,若是調用Activity.finish();將Activity銷燬:當GC線程開始工做時,會從GC Roots開始檢索引用鏈,就會發現Activity會被上圖中的一條引用鏈所鏈接,若是Message此時還在消息隊列中(試着發送一條延遲5min的消息,那麼消息會一直在隊列中直到5min後這條消息被取出交給Handler處理,在這5min以內Activity被finish掉,可是Activity指向的內存區域是沒法回收的,也就是發生了內存泄漏),則這條引用鏈會一直引用着Activity,使Activity的這塊堆內存空間沒法回收,致使內存泄漏。因此,解決方法就要從源頭解決,斷開引用鏈:ide

  1. 在Activity的onDestroy回調中調用handler.removeCallbacksAndMessages(斷裂上面引用鏈的第2個箭頭)
@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
複製代碼
  1. 使用靜態內部類,內部類中使用Activity弱引用(由於收到消息後通常須要用到Context來處理UI或者彈Toast)(斷裂上面引用鏈的第五個箭頭)

下面是基於這種方法封裝的一個SafeHandler,你們若是能理解下面這個封裝的SafeHandler類,那麼Android 的Handler內存泄漏知識點就完全掌握了!:oop

package com.wiz.car.common.util;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

import java.lang.ref.WeakReference;

/** * 弱引用封裝Handler * @param <T> */
public abstract class SafeHandler<T> extends Handler {

    private final WeakReference<Context> mReference;
    private final WeakReference<T> mReferenceT;

    public SafeHandler(Context context, T t) {
        mReference = new WeakReference<>(context);
        mReferenceT = new WeakReference<>(t);
    }

    /** * 執行體 * @param t */
    public abstract void execute(T t);

    private Runnable innerRun = new Runnable() {

        @Override
        public void run() {
            Context context = mReference.get();
            if(context != null) {
                if(context instanceof Activity && ((Activity)context).isFinishing()){
                    release();
                    LogUtils.w("SafeHandler :","The task executed, but "+context.getClass().getSimpleName()+" is finishing!");
                }else {
                    T t = mReferenceT.get();
                    if(t != null) {
                        execute(t);
                        LogUtils.w("SafeHandler :","The task executed in "+context.getClass().getName());
                    }else{
                        LogUtils.w("SafeHandler :","The task executed, but callback class is null");
                    }
                }
            }else {
                release();
                LogUtils.w("SafeHandler :","The task executed, but activity destroyed!");
            }
        }
    };

    /** * 暫停任務 */
    public void stop() {
        removeCallbacks(innerRun);
    }

    /** * 銷燬任務 */
    public void release(){
        stop();
        mReference.clear();
        mReferenceT.clear();
    }

    /** * 延遲執行 * @param time 延遲時間 */
    public void postDelayed(long time) {
        stop();
        postDelayed(innerRun, time);
    }

}

複製代碼
相關文章
相關標籤/搜索