Handler引发的内存泄露原因及解决方案

Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

1.消息的表示:Message
2.消息队列:MessageQueue
3.消息循环,用于循环取出消息进行处理:Looper
4.消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler

平时我们最常使用的就是Message与Handler了,Handler通过sendMessage函数发送一条Message,该Message进入MessageQueue消息队列,Looper在消息队列中循环取出Message进行处理,以上基本就是消息处理的原理机制。

那个和内存泄露又有什么关系呢?我们接下来继续看。


当Android程序第一次创建的时候,在【主线程】同时会创建一个Looper对象。Looper实现了一个简单的MessageQueue消息队列,一个接着一个处理Message对象。程序框架所有主要的事件(例如:屏幕上的点击时间,Activity生命周期的方法等等)都包含在Message对象中,然后添加到Looper的消息队列中,一个一个处理。主线程的Looper存在整个应用程序的生命周期内。

如果是在【子线程】中创建Handler就需要我们自己创建Looper对象维护队列来处理消息,不然就会出现
- java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare();
这些不在我们今天介绍的范围之内,在此只是做一个简单说明。

当一个Handler对象在主线程中创建的时候,它会关联到LooperMessageQueue消息队列。Message添加到消息队列中的时候Message会持有当前Handler引用,当Looper处理到当前消息的时候,会调用Handler的handleMessage(Message)来进行消息的处理

注意:Message会持有当前Handler引用

当我们在Activity的主线程中以这种方式创建Handler对象时 Android Lint工具会提示我们需要static:

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

为什么会有这样的提示呢,因为这时的Handler对象是Activity的内部类,在java中非静态内部类隐式的持有外部的引用,也就是Activity的引用。

注意:Handler会持有当前Activity引用


那么这个时候就会发生一个连锁反应

Message—–引用—–>Handler对象—–引用—–>Activity对象

如果我们执行的是一个比较耗时的操作Message进入MessageQueue队列中需要一段时间A才能由Looper对象取出交给Handler来处理,在A时间内我们退出Activity那么就会因为上诉原因产生内存泄露。所以Lint工具会提示我们需要static,这样就不会持有外部的引用了。

那么我们分析完了原因怎么来解决呢?其实答案已经很明显了。

我们这里分成两部分来解决问题
1.解决Message会持有当前Handler引用

这个问题解决起来相对简单,既然Message会持有当前Handler引用,一般情况下我们再退出Activity的时候就不在需要Handler处理Message消息了,所以就需要将Message从Handler中剔除出来,并将handler对象置空

这里写图片描述

handler可以通过以上方法在Activit销毁的时候移除相应的Message对象,然后将handler对象置空

@Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeXXX();
        handler = null;
    }

2.解决Handler持有当前Activity引用

既然java的非静态内部类会隐式持有外部对象,那么我们就将Handler内部类变成静态内部类,然后在用new的方式创建Handler对象

private static class MyHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {

    }
}

MyHandler handler = new MyHandler();

我们用Handler对象处理消息经常要和外部类交互啊,这样静态了我们再handleMessage(Message msg)中调用外部类中的非静态方法都会报错啊,怎么办?

弱引用——-我们还是要持有外部类的对象,但是用弱引用的方式持有,这样外部类Activity销毁的时候我们的Handler就不再持有外部引用了。

private static class MyHandler extends Handler{
    WeakReference<MainActivity> mActivity;

    public MyHandler (MainActivity activity) {
        mActivity = 
            new WeakReference<MainActivity>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        //利用mActivity调用方法;
        mActivity.get().XXXX();
    }
}

MyHandler handler = new MyHandler(this);

以上两个方法就可以解决Handler引发的内存泄露,水平有限,如有问题可以评论中指正,期待和大家共同进步。

猜你喜欢

转载自blog.csdn.net/omyrobin/article/details/51805746