WeakHandler 是怎么解决 Handler 的内存问题的

内存泄露

什么是内存泄露,请参考Java的内存泄漏

Handler内存泄露

对于安卓的初学者,常见的handler的写法如下:

 

public class SampleActivity extends Activity {

 

private final Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

// ...

         }

     }

}


Runnable内存泄露在上面的代码中,mHandler作为SampleActivity的内部匿名类的对象,持有对父类对象的引用。为了保证在子线程(一般是后台线程,可能比较耗时)能够通过Handler和主线程进行通讯,一般该子线程需要持有对应Handler的对象。因此,如上的代码即使SampleActivity被关闭,mHandler由于子线程的引用并不会被回收,同样mHandler厘面引用的SampleActivity对象也不会被回收(内存泄露)。

在发起一个延时操作时,通常会这样写:

mHandler.postDelayed(new Runnable() {
  @Override
  public void run() { /* ... */ }
}, 10 * 60 * 1000);


有一些文章说这种情况也会造成mHandler的泄露,这一点我不太认同,因为Message对象或者后面保存Message的MessageQueue本身都没有持有对mHandler的引用,所以按理说这里应该不会造成mHandler的泄露。这里的实现Runnable接口的内部匿名类同样也持有了外部类(通常是Activity)的引用,这个对象作为Message的属性会一直存在直到达到指定的延时时长。这就很有可能在Activity被关闭时导致Activity不能及时被回收。

解决方案

对于上面的问题,已经可以找到比较多的解决方案,如下:

  • 静态内部类或独立类,一般针对Handler泄露的方案。既避免使用非静态内部类,这样Handler的对象就不会因为持有外部Activity的对象而造成泄露。
  • 弱引用,能够从根源处解决内存泄露问题。为了编写代码的方便性,一般不建议Runnable使用静态内部类或独立类来解决Runnable的泄露问题,而是通过让系统Message持有一个弱引用(参考)来解决这个问题。
  • 在逻辑上控制,这个受限于业务的限制,不过如果逻辑允许的话,可以在页面关闭时将对应的线程结束,或者将Runnable对象从队列中移除mHandler.removeCallback()

WeakHandler

下面图片显示WeakHandler的实现逻辑

ScreenshotScreenshot

解决Handler内存泄露

WeakHandler强引用一个Handler子类(ExecHandler)的对象,然后通过自定义的一个Callback将Handler的消息处理转发到这个callback中,这样就不必为了处理消息而构建一个匿名内部handler类对象。WeakHandler对象(通过ExecHandler对象)仅仅维持对callback对象的弱引用。这样即使callback对象持有对Activity对象的引用,由于其本身不会产生泄露,因此

 

private static class ExecHandler extends Handler {
    private final WeakReference<Callback> mCallback;
    ExecHandler() {
        this.mCallback = null;
    }
    ExecHandler(WeakReference<Callback> callback) {
        this.mCallback = callback;
    }
    ExecHandler(Looper looper) {
        super(looper);
        this.mCallback = null;
    }
    ExecHandler(Looper looper, WeakReference<Callback> callback) {
        super(looper);
        this.mCallback = callback;
    }
    public void handleMessage(@NonNull Message msg) {
        if(this.mCallback != null) {
            Callback callback = (Callback)this.mCallback.get();
            if(callback != null) {
                callback.handleMessage(msg);
            }
        }
    }
}


解决Runnable泄露问题但是,WeakHandler的对象会维持对callback对象的强引用。

WeakHandler内部定义一个WeakRunnable用来包装我们传递进去的Runnable对象,在WeakRunnable中维持对Runnable对象的弱引用,从而解决了Runnable对象不释放而造成的内存泄露问题。

static class WeakRunnable implements Runnable {
    private final WeakReference<Runnable> mDelegate;
    private final WeakReference<WeakHandler.ChainedRef> mReference;
    WeakRunnable(WeakReference<Runnable> delegate, WeakReference<WeakHandler.ChainedRef> reference) {
        this.mDelegate = delegate;
        this.mReference = reference;
    }
    public void run() {
        Runnable delegate = (Runnable)this.mDelegate.get();
        WeakHandler.ChainedRef reference = (WeakHandler.ChainedRef)this.mReference.get();
        if(reference != null) {
            reference.remove();
        }
        if(delegate != null) {
            delegate.run();
        }
    }
}


注意事项记住,WeakHandler的对象会维持对runnable对象的强引用。

在使用WeakHandler时,应该在activity(或者fragment)中声明一个全局变量,以保证WeakHandler的生命周期和activity保持一致。

 

Activity{
	private WeakHandler mHandler = new WeakHandler();
}


或者如果定义一个静态变量的话,会导致callback和runnable不能被释放,从而导致内存泄露。如果只是定义一个临时变量,在内存不足时handler会被回收,导致callback和runnable对象也会被回收,从而不能拿到回调。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值