Handler内存泄漏及其解决方案

时间:2020-11-27 20:56:15

几个概念

Java垃圾回收机制(GC)

Java使用自动垃圾回收机制,回收的条件就是对象是否被引用。也就是说如果对象处于不可到达状态就会被回收掉。

Java四种引用

Handler内存泄漏及其解决方案

Activity回收

Activity被销毁的时候在onDestory()方法中,系统销毁了这个Activity的实例在内存中占据的空间。在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了。

Handler内存泄漏的原因?

Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        //修改Activity的UI
        mImageView.setImageBitmap(mBitmap);
    }
};
  1. 当使用内部类(包括匿名类)来创建Handler的时候,因为通过Handler来操作Activity中的View,所以Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用。
  2. Handler通常会伴随着一个耗时的后台线程(从网络下载图片),这个后台线程在任务执行完毕之后(图片下载完毕),通过消息机制通知Handler,然后Handler把图片更新到界面。
  3. 如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,在onDestory()方法中执行GC检查时就应该被回收掉。但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收,造成内存泄漏,直到网络请求结束(图片下载完毕)。
  4. 如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
  5. 同一个Acticity反复进行几次便会程序占用内存超过系统限制,FC。

解决方案

逻辑控制

  1. 在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。

  2. 如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

Handler声明为静态类

静态类不持有外部类的对象,所以你的Activity可以随意被回收,所以不会导致外部类实例出现内存泄露。

static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        //修改Activity的UI
        mImageView.setImageBitmap(mBitmap);
    }
}

但是,Handler一旦无法持有外部类的对象,就无法修改Activity的View,那么使用Handler就没有意义了。
为了解决这个问题,我们可以在Handler中增加一个对Activity的弱引用(WeakReference):

static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;

    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}

当Activity finish后 handler对象还是在Message中排队。 还是会处理消息,这些处理有必要?
正常Activitiy finish后,已经没有必要对消息处理,那需要怎么做呢?
解决方案也很简单,在onDestroy的时候,取消掉该Handler对象的Message和Runnable。
removeCallbacks(Runnable r)和removeMessages(int what)等。

@Override
public void onDestroy() {
    //If null, all callbacks and messages will be removed.
    mHandler.removeCallbacksAndMessages(null);
}

参考引用

https://blog.csdn.net/jdsjlzx/article/details/51386440