自定义 Handler 时如何有效地避免内存泄漏问题?

如何避免自定义handler内存泄漏

你好!

名词解释

内部类(点击跳转):
https://blog.csdn.net/chaseDreamer_/article/details/102859467

简介

在Android系统中,Handler是一个消息发送和处理机制的核心组件之一,与之配套的其他主要组件还有Looper和Message,MessageQueue。
Message和Runnable类是消息的载体。MessageQueue是消息等待的队列。Looper则负责从队列中取消息。

作用

  • 安排调度(scheule)消息和可执行的runnable,可以立即执行或者定时在未来的某个时间执行
  • 让某个行为(action)在其他线程执行

关于泄漏

Handler是由系统提供的异步消息处理的一种常用的方式,一般情况下不会导致内存泄漏,至于其为什么可能会导致内存泄漏,这里的内存泄漏常常指的是泄露了Acitivity等组件

public class HandlerTestActivity extends Activity{
    public Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
             
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
	}
}

这个写法有什么问题呢?这个问题就在于该Handler的实例采用了内部类的写法,它是HandlerTestActivity这个实例的内部类,在Java中,关于内部类有一个特点:在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引用,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。

生命周期较短的组件引用了生命周期较长的组件。Handler就是一种典型的示例,以上面的代码举例。HandlerTestActivity可能会被泄漏,也就是该组件没有用了,比如调用了finish()后,垃圾回收器却迟迟没有回收该Activity。原因出在该实例的handler内部类引用了它,而该handler实例可能被MessageQueue引用着。其实正常情况下是不会内存泄漏的,除非handler队列等待太久,想要解决handler导致的内存泄漏,你就需要理解泄漏的原因,(handler持有activity,然后message持有handler,然后MessgQuene持有message),理解了这样持有链,那么handler持有activity可以通过弱引用或静态内部类等方式解决,而removeCallbacksAndMessages则是清除后面的引用,其实如果你的handler中没有什么耗时操作,任务完成了handler就会释放Activity的引用就不会导致内存泄漏了;或者你的handler中是一个2秒的操作,在Activity退出的2s后,释放了Activity的引用,这种情况属于短时间内存泄漏,它也会释放的,所以也可以不管,但是如果你的handler中是死循环啥的的话,就会导致内存泄漏了。

从上面的说法中,可以思考得到相应的解决方法:

1.保证Activity被finish()时该线程的消息队列没有这个Activity的handler内部类的引用。这个场景是及其常见的,因为handler经常被用来发延时消息。一个补救的办法就是在该类需要回收的时候,手动地把消息队列中的消息清空:mHandler.removeCallbacksAndMessages(null);

2.要么让这个handler不持有Activity等外部组件实例,让该Handler成为静态内部类。(静态内部类是不持有外部类的实例的,因而也就调用不了外部的实例方法了)

3.在2方法的基础上,为了能调用外部的实例方法,传递一个外部的弱引用进来)

4.将Handler放到抽取出来放入一个单独的顶层类文件中。

这里需要了解一下关于Java里面引用的知识:

  • 强引用(Strong Reference) 默认引用。如果一个对象具有强引用,垃圾回收器绝不会回收它。在内存空
    间不足时,Java虚拟机宁愿抛出OutOfMemory的错误,使程序异常终止,也不会强引用的对象来解决内存不足问题。
  • 软引用(SoftReference) 如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
  • 弱引用(WeakReference) 在垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 虚引用(PhantomReference) 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

第三种,需要一些额外的代码,比较通用。

public class HandlerTestActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<HandlerTestActivity> mActivity;
public MyHandler(HandlerTestActivity activity) {
  mActivity = new WeakReference<HandlerTestActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
  ShanActivity activity = mActivity.get();
  if (activity != null) {
     //do Something
  }
}
}

第四种方式,抽取做单独封装。

/**
 * 实现回调弱引用的Handler
 * 防止由于内部持有导致的内存泄露
 * 传入的Callback不能使用匿名实现的变量,必须与使用这个Handle的对象的生命周期一 
 * 致否则会被立即释放掉了
 */
public class WeakRefHandler extends Handler {
    private WeakReference<Callback> mWeakReference;
    
    public WeakRefHandler(Callback callback) {
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    public WeakRefHandler(Callback callback, Looper looper) {
        super(looper);
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    @Override
    public void handleMessage(Message msg) {
        if (mWeakReference != null && mWeakReference.get() != null) {
            Callback callback = mWeakReference.get();
            callback.handleMessage(msg);
        }
    }
}

由于是弱引用,当该类需要被回收时,可以直接被回收掉。

WeakRefHandler的使用时如下:

  private Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch(msg.what){
            }
            return true;
        }
    };
    private Handler mHandler = new WeakRefHandler(mCallback);

我是入梦,谢谢你的观看我的博客,如果有什么错误,请随时联系我,QQ:897589417

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值