Android非静态Handler内存泄漏快速理解及解决

非静态Handler可能引起的内存泄漏的原因

Looper、MessageQueue、Message介绍:

当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper(其中包含MessageQueue)实例。
Looper的主要工作就是一个一个处理消息队列MessageQueue中的消息对象。
在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息Message中,然后加入到 Looper要处理的消息队列MessageQueue中,由Looper负责一条一条地进行处理。
主线程中的Looper(包含Looper中的MessageQueue)生命周期和当前应用一样长。

Handler的持久引用分析

Message发送及执行过程

1、Handler在主线程进行了初始化
2、Handler发送Message
3、Message进入MessageQueue
4、Looper从Handler中取出Message
5、Message使用Handler中handleMessage方法执行

Handler、Message、MessageQueue、Looper引用关系

对应以上Message发送及执行过程

1、Handler在主线程进行了初始化:Handler持有主线程中Looper,并获取Looper对应MessageQueue
2、Handler发送Message:Message的target成员变量持有Handler(不考虑Message的setTarget方法)
3、Message进入MessageQueue:MessageQueue持有Message
Looper从Handler中取出Message:MessageQueue释放Message
4、Message使用Handler中handleMessage方法执行:执行完成后Message不再被引用、同样的Message持有的Handler不再被引用

Handler持久引用结论

由以上分析可知:
当Handler发送Message后,在Message被处理前,Handler会一直被强引用

静态/非静态Handler区别 及 内存泄漏的原因

【静态Handler】:创建实例时,不持有创建时所处对象的实例(Activity、View、Dialog等)
【非静态Handler】:创建实例时,持有创建时所处对象的实例(Activity、View、Dialog等)
所以,非静态Handler发送的Message被执行前,创建Handler的实例对象(Activity、View、Dialog等)会被一直引用。
尤其当Handler发送的Message为延时消息时,创建Handler的实例对象可能会被长时间无效引用,导致内存泄漏。
无效引用:当实例对象的生命周期完成时,比如Activity的onDestroy()完成,之前发送的Message被执行已经无意义,对实例对象的引用即为无效引用。

解决方案: WeakReference解决可能的内存泄漏
官方解决方案
以下代码来自Android源码,api-24,android.app.Dialog

private static final class ListenersHandler extends Handler {
    private final WeakReference mDialog;

    public ListenersHandler(Dialog dialog) {
        mDialog = new WeakReference<>(dialog);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DISMISS:
                ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                break;
            case CANCEL:
                ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                break;
            case SHOW:
                ((OnShowListener) msg.obj).onShow(mDialog.get());
                break;
        }
    }
}

扩展的解决方案:(以下代码可直接复制使用)
【第一步】:创建公共Handler

package com.love;

import android.os.Handler;
import android.os.Message;

import java.lang.ref.WeakReference;

/**
 * Handler,防止内存泄露
 * 使用方式:声明静态内部类继承此类
 * Date: 2018/3/21
 */
public abstract class StaticHandler extends Handler {
    private WeakReference mTargets;

    public StaticHandler(T target) {
        mTargets = new WeakReference<>(target);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        T target = mTargets.get();
        if (target != null) {
            handle(target, msg);
        }
    }

    public abstract void handle(T target, Message msg);
}

【第二步】:创建静态内部Handler继承公共Handler,并使用(示例)

package com.love;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;

/**
 * 演示静态Handler使用
 * 

* Date: 2018/3/21 */ public class TestActivity extends Activity { static final int WHAT_TOAST = 1; public static class MyHandler extends StaticHandler { public MyHandler(TestActivity target) { super(target); } @Override public void handle(TestActivity target, Message msg) { switch (msg.what) { case WHAT_TOAST: target.showToast(); break; } } } private Handler mHandler = new MyHandler(this); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler.sendEmptyMessageDelayed(WHAT_TOAST, 1000); } public void showToast() { //do what you want } }

你可能感兴趣的:(Android非静态Handler内存泄漏快速理解及解决)