Android handler的Context内存泄露

先看下面这段代码

public class LeakActivity extends Activity {

    public Handler leakHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
        }
    };

这里简单的声明了一个handler,看起来一切正常,但是这种写法有可能会引起很大的内存泄露,原因其实编译器已经告诉你了,因为这种写法往往有一个警告:
这里写图片描述

在Android中 Handler应该被声明为 static 否则可能会引起内存泄露的问题

为什么可能会内存泄露呢? 我们来分析一些已有信息

  1. 当一个Android应用第一次启动的时候,系统会创建一个Loop对象在应用的主线程中,Loop就是一个消息队列(Message queue),处理一个又一个的信息。应用的主要事件比如:Activity的生命周期,点击事件等等,都包含在消息(message)对象中。消息会在Loop 的消息队列中排队等着被处理,所以这个主线程的Loop会在应用的整个生命周期中始终存在

  2. 现在我们的handler已经在主线程声明,很自然的他与主线程Loop的消息队列关联了起来。这时传到消息队列中的消息会带有handler的引用,因为当消息被处理时系统需要自己去调用Handler的handleMessage(Message msg)方法

  3. 在java中 非静态的内部匿名类(也就是我们声明Handle的方式),会持有他的外部类的一个隐式的应用,当然静态的内部类是不会的。

基于以上几点修改一下这个例子

public class LeakActivity extends Activity {

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

    protected void onCreate(android.os.Bundle savedInstanceState) {
        setContentView(R.layout.activity_leak);
        //10分钟之后发送消息 
        leakHandler.postDelayed(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub

            }
        }, 1000 * 60 * 10);
        //退出到上一个Activity
        finish();
    };

声明的handler延时10分钟发送信息,同时finish()掉当前的Activity,根据上面说明的第二点,延时10分钟发送的信息,依然会在主线程的消息队列中并且等待10分钟后发送信息。那么问题来了由于消息持有handler的引用,而handler又是一个匿名内部类,匿名内部类会持有他的外部类(也就是LeakActivity)的一个隐式引用,而LeakActivity已经被finish()掉了,所以 就有可能会导致Context的内存泄露,注意一下上面的runnable也是一个匿名内部类

如何解决这个隐患呢,方法相信大家已经知道了,使用静态内部类。这样就不会去持有外部的隐式引用。

public class LeakActivity extends Activity {

  /**
   * 声明静态内部类不会持有外部类的隐式引用
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

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

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * 这里的Runnable也是 
   * 声明静态内部类不会持有外部类的隐式引用
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //10分钟之后发送消息 
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // 退回到上一个Activity
    finish();
  }
}

将handler和runnable都声明为静态内部类,同时让handler持有的Context声明为WeakReference 。

FBI Warning

如果内部类可以存在于activity的生命周期之外,那么不要去使用非静态的内部类,因为他会持有外部类(activity)的隐式引用。把它声明成静态的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值