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

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

本文目的:使读者快速理解 并 使用提供的解决方案

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

Looper、MessageQueue、Message介绍

  1. 当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper(其中包含MessageQueue)实例。
  2. Looper的主要工作就是一个一个处理消息队列MessageQueue中的消息对象。
  3. 在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息Message中,然后加入到 Looper要处理的消息队列MessageQueue中,由Looper负责一条一条地进行处理。
  4. 主线程中的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
  4. Looper从Handler中取出Message:MessageQueue释放Message
  5. 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<DialogInterface> 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/5/26
 *
 * @author lichuang
 */
public abstract class StaticHandler<T> extends Handler {
    private WeakReference<T> 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使用
 * <p>
 * Date: 2018/5/26
 *
 * @author lichuang
 */
public class TestActivity extends Activity {
    static final int WHAT_TOAST = 1;

    public static class MyHandler extends StaticHandler<TestActivity> {

        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
    }
}

猜你喜欢

转载自my.oschina.net/u/2393003/blog/1819084
今日推荐