This Handler class should be static or leaks might occur

原文链接:http://my.oschina.net/rengwuxian/blog/181449

一、现象

当使用内部类(包括匿名内部类)来创建 Handler 时,我们会看到一条警告信息:This Handler class should be static or leaks might occur。翻译过来大概是:这个 Handler 对象应该是静态的,否则有可能造成内存泄露。

二、原理

内存泄露是什么

Java 有自己的垃圾回收机制(gc),gc 会检查内存中的对象(何时检查由 gc 自己决定),如果对象满足下列两种情况之一,gc 就会回收该对象:

  • 该对象未被任何其他对象持有(即其他对象没有该对象的引用)
  • 该对象在的一组对象中,该组对象只有相互之间的引用

而内存泄露就是 gc 未能成功回收某些应该被回收的对象,导致内存池中对象越来越多,最终突破 Android 对单个 app 的内存限制(一般是 24m),造成内存溢出,也就是我们常说的 OOM(out of memory)。

Handler 为何会造成内存泄露

1.Handler 与 Thread 配合实现异步任务

比如我们通过 Handler 和 Thread 实现一个网络请求,在请求的过程中,Activity 被关闭了(比如用户按了返回键),那么在 gc 检查时这个 Activity 是应该被回收掉的,但是你的网络请求(Thread)并未结束,也就是 Thread 持有 Handler,而 Handler 又持有 Activity,从而形成了一条 Thread→Handler→Activity 的链,导致 Activity 未能被成功回收,这就出现了内存泄露。

2.Handler 调用 postDelay()方法

postDealy()方法会把你的 Handler 对象装入一个 Message 对象中,然后将这个 Message 对象推入 MessageQueue 队列中,假如你的 Activity 关闭时,delay 的时间还没到,就会形成一条 MessageQueue→Message→Handler→Activity 的链,导致 gc 不能成功回收 Activity,从而造成内存泄露。

三、解决方案

1.完善程序逻辑

在 Activity 被关闭(onDestory()方法)时关闭或移除某些对象,从而避免对 Activity 的引用链。

  • 使用 Handler 和 Thread,在 Activity 被关闭时将网络连接切断即可。
  • 使用 postDealy 方法,在 Activity 被关闭时调用相应 Handler 的 removeCallbacks()方法将 Message 从 MessageQueue 中移除即可。

2.将 Handler 声明为静态类

静态类本身不会持有外部类对象,也就是说,在 Activity 中的 Handler 内部类如果被声明为静态的,就不会持有外部的 Activity 对象了。然而,这样一来 Handler 同时也不能像平常一样操作 Activity 中的那些非静态对象了,解决方法是在静态 Handler 类中声明一个对外部 Activity 的弱引用(关于弱引用的解释在文章底部),实现代码如下:

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);
        }
    }
}

四、什么是弱引用(WeakReference)

弱引用 WeakReference,与强引用(即我们常说的引用)相对,它的特点是,gc 在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向,该对象就会在被 gc 检查到时回收掉。因此,在 Handler 中声明一个对 Activity 的弱引用,既可以操作 Activity 中的非静态对象,又可以避免因 Handler 对 Activity 的强引用造成的内存泄露。