你真的会用户Handler吗?

大家应该很喜欢吧 Handler写成一个内部类譬如:

private Handler mMainActivityHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
      super.handleMessage(msg);
 }
};

其实包括我也很喜欢,而且一个Activity 对应一个 Handler,每一个 Handler 负责更新本 Activity 的 UI,一对一关系,分工明确。好用到爆炸。

然而 java 内部类是默认持有一个外部类的引用,因为 jvm 在把.java 源文件编译成 .class 字节码的时候,会在默认的构造函数加入外部类的引用。所以我们在内部类中也能访问外部类的引用。

然后问题就发生了,当前 Handler 持有当前 Activity 的引用,Handler 不释放,Activity 也别想释放了。

当Android应用首次启动时,framework会在应用的UI线程创建一个Looper对象。Looper实现了一个简单的消息队列并且一个接一个的处理队列中的消息。应用的所有事件(比如Activity生命周期回调方法,按钮点击等等)都会被当做一个消息对象放入到Looper的消息队列中,然后再被逐一执行。UI线程的Looper存在于整个应用的生命周期内。

当在UI线程中创建Handler对象时,它就会和UI线程中Looper的消息队列进行关联。发送到这个消息队列中的消息会持有这个Handler的引用,这样当Looper最终处理这个消息的时候framework就会调用Handler#handleMessage(Message)方法来处理具体的逻辑。
在Java中,非静态的内部类或者匿名类会隐式的持有其外部类的引用,而静态的内部类则不会。

 1public class SampleActivity extends Activity {
2
3  private final Handler mLeakyHandler = new Handler() {
4    @Override
5    public void handleMessage(Message msg) {
6      // ...
7    }
8  }
9
10  @Override
11  protected void onCreate(Bundle savedInstanceState) {
12    super.onCreate(savedInstanceState);
13
14    // Post a message and delay its execution for 10 minutes.
15    mLeakyHandler.postDelayed(new Runnable() {
16      @Override
17      public void run() /* ... */ }
18    }, 1000 * 60 * 10);
19
20    // Go back to the previous Activity.
21    finish();
22  }
23}

当activity被finish的时候,延迟发送的消息仍然会存活在UI线程的消息队列中,直到10分钟后它被处理掉。这个消息持有activity的Handler的引用,Handler又隐式的持有它的外部类(这里就是SampleActivity)的引用。这个引用会一直存在直到这个消息被处理,所以垃圾回收机制就没法回收这个activity,内存泄露就发生了。需要注意的是:15行的匿名Runnable子类也会导致内存泄露。非静态的匿名类会隐式的持有外部类的引用,所以context会被泄露掉

解决办法:

声明为静态类以后,Handler就没有了Activity的引用,无法直接引用其变量或方法了,因此我们在这里使用弱引用WeakReference来解决这个问题:

static class MyHandler extends Handler {

    WeakReference<MainActivity> activity;    public DownloadHandler(MainActivity mainActivity) {
        activity = new WeakReference<MainActivity>(mainActivity);
    }    @Override
    public void handleMessage(Message msg) {  }
}

WeakReference可以尽早地被GC回收掉,因此可以防止内存泄漏。由于这里的activity变量实际不是MainActivity类型的,因此在handleMessage中还要多一步获取与判断:

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

当然每次这样调用比较麻烦,可以将WeakReference声明初始化在MainActivity中,在Handler初始化时直接传入:

public class MainActivity extends Activity {

    WeakReference<MainActivity> activity;
    MyHandler mHandler;    @Override
    protected void onCreate(Bundle savedInstanceState) {
        activity = new WeakReference<MainActivity>(mainActivity);
        mHandler = new MyHandler(activity);
    }

    class MyHandler extends Handler {        private MainActivity activity;        public MyHandler(WeakReference<MainActivity> ref) {
            activity = ref.get();
        }        @Override
        public void handleMessage(Message msg) {            if(activity != null) {
                activity.someMethod();
            }
        }
    }
}

                        喜欢 就关注吧,欢迎投稿!

640?wx_fmt=jpeg

本网站文章均为原创内容,并可随意转载,但请标明本文链接
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/你真的会用户handler吗?/

此条目发表在Android分类目录。将固定链接加入收藏夹。

发表评论

您的电子邮箱地址不会被公开。