优雅地使用Handler,避免内存溢出、空指针

在Activity中直接创建Handler的内部类,比如这样:

public class HandlerActivity extends AppCompatActivity {

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            doSomething();
        }
    };

    private void doSomething() {}

}

这时IDE会有黄色的提示:
This Handler class should be static or leaks might occur (null) less... (Ctrl+F1)

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

非静态的内部类会隐式地持有外部类的引用,这会导致外部类对象无法被系统的GC回收。
如果你的Handler是在主线程外的线程创建的,那就没问题。
如果你的Handler就是在主线程创建的,那就应该使用静态内部类+弱引用的方式来持有外部类引用。
举个例子:
你的Handler在子线程执行了耗时操作再sendMessage,或者发送的Message直接就是延时执行的,如:
mHandler.sendEmptyMessageDelayed(1,5000);//5秒后执行
如果这时候,用户按了返回键,撤销了这个Activity,就会可能导致内存泄漏,因为mHandler持有了Activity的引用,而Message又持有了Handler的引用,这个Message在MessageQueue中队列,直到时间到了才会被handleMessage。而在这段时间内,Activity是无法被GC回收掉的。

根据提示,修改如下:
public class HandlerActivity extends AppCompatActivity {

    private SafeHandler mSafeHandler = new SafeHandler(this);

    /**根据提示实现的Handler*/
    private static class SafeHandler extends Handler {

        private WeakReference<HandlerActivity> mWeakReference;

        public SafeHandler(HandlerActivity activity) {
            super();
            mWeakReference = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = mWeakReference.get();
            if (activity != null) {
                activity.doSomething();
            }
        }
    }

    private void doSomething() {}

}

通过静态内部类+弱引用来持有对象,确保Activity能被及时回收掉,在handleMessage时获取对象,再先判空操作,防止空指针异常。

虽然重复代码只有几行,不过还是可以抽取为基类的,使用的时候依然是采用静态内部类来继承基类:

/**
 * Handler的封装
 * 子类应为静态内部类
 *
 * @param <T> 外部类
 */
public abstract class BaseHandler<T>
        extends Handler {
    
    private final WeakReference<T> mReference;
    
    public BaseHandler(T t) {
        super(Looper.getMainLooper());
        mReference = new WeakReference<>(t);
    }
    
    @Override
    public void handleMessage(Message msg) {
        T t = mReference.get();
        if (t != null) {
            handleMessage(t, msg);
        }
    }
    
    protected abstract void handleMessage(T t, Message msg);
}

外部类使用时,如下所示:

public class MainActivity
        extends CoreActivity {
    
    private Handler mHandler = new MyHandler(this);
    
    private static final class MyHandler
            extends BaseHandler<MainActivity> {
        
        public MyHandler(MainActivity mainActivity) {
            super(mainActivity);
        }
        
        @Override
        protected void handleMessage(MainActivity mainActivity, Message msg) {
            
        }
    
}


注:测试时会发现Activity总是不为NULL,那是因为系统没有调用GC回收。
使用DDMS主动调用几次Cause GC就行了。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值