Android中Handler引起的内存泄露

在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用。通常我们的代码会这样实现。

但是,其实上面的代码可能导致内存泄露,当你使用Android lint工具的话,会得到这样的警告

In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class

看到这里,可能还是有一些搞不清楚,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下。

1.当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长。

2.当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用Handler#handleMessage(Message)完成消息的正确处理。

3.在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。关于这一内容可以查看细话Java:”失效”的private修饰符

确实上面的代码示例有点难以察觉内存泄露,那么下面的例子就非常明显了

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  }

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

    // Post a message and delay its execution for 10 minutes.
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { /* ... */ }
    }, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

分析一下上面的代码,当我们执行了Activity的finish方法,被延迟的消息会在被处理之前存在于主线程消息队列中10分钟,而这个消息中又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的SampleActivity的引用,所以这导致了SampleActivity无法回收,进行导致SampleActivity持有的很多资源都无法回收,这就是我们常说的内存泄露。

注意上面的new Runnable这里也是匿名内部类实现的,同样也会持有SampleActivity的引用,也会阻止SampleActivity被回收。

要解决这种问题,思路就是不适用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。注意:一个静态的匿名内部类实例不会持有外部类的引用。 修改后不会导致内存泄露的代码如下:

public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  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);

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

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

    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

其实在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,就像本文提到的一样,所以当我们使用时要非静态内部类时要格外注意,如果其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。个人倾向于使用文章的静态类和弱引用的方法解决这种问题。

译文信息

时间: 2024-08-05 02:33:24

Android中Handler引起的内存泄露的相关文章

Android 中 Handler 引起的内存泄露

在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.其实这可能导致内存泄露,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下.http://www.jinhusns.com/Products/Download/?type=xcj 在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. public class SampleActivity extends Activity {   

Android中Handler导致的内存泄露

http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html Consider the following code: 1 2 3 4 5 6 7 8 9 public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public voi

Android开发——Handler引起的内存泄露

在Android异步消息处理中, Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // } }; 但当我们这么写时,编译器会给出警告提示:Handler类应该是静态的,可能发生内存泄漏. 原因: 在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用.静态的内部类不会持有外部类的引用. 这里Handler是一个匿名内部类的实例,其持有外面的Activity的引

Android中使用Handler引发的内存泄露

转载请注明出处:http://blog.csdn.net/allen315410/article/details/43638373 本文翻译自:国外某位开发者的博客How to Leak a Context: Handlers & Inner Classes,英文可以的朋友可以直接点击原文查看. 在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. public class SampleActivity extends Activity

Android OOM(Out of memory) 内存泄露基本知识

Android OOM(Out of memory) 内存泄露基本知识 转载至:http://blog.csdn.net/emilychai2010/article/details/12710135 1. 内存泄露 2. Android里的垃圾回收 3. Heap 4. 调试 5. 常见的内存泄露 1.什么是内存泄露(memory leak)? A "memory leak" in your code is when you keep a reference to an object

在java中如何创建一个内存泄露

今天访问java 并发编程网,看到一个翻译征集令,并发编程网的作者从stackoverflow 网站上选取了一些经典问答,遂决定翻译几篇 征集令地址:http://ifeve.com/stackoverflow-assembly/ 翻译系列文章: 1.Java 核心类库中的一些设计模式 2. hashMap 与hashTable之间的区别 3.  在java中如何创建一个内存泄露 译文: 在java中如何创建一个内存泄露 问题: 我之前参加了一个面试, 被问到在java中如何创建一个内存泄露.不

深入解析Android中Handler消息机制

Android提供了Handler 和 Looper 来满足线程间的通信.Handler先进先出原则.Looper类用来管理特定线程内对象之间的消息交换(MessageExchange).Handler消息机制可以说是Android系统中最重要部分之一,所以,本篇博客我们就来深入解析Android中Handler消息机制. Handler的简单使用 为什么系统不允许子线程更新UI 因为的UI控件不是线程安全的. 如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访

Android中Handler的使用方法——在子线程中更新界面

本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Han

异步消息处理机制-Android中Handler原理(续)

异步消息处理线程是指线程启动后会进入一个无限循环,每循环一次,从内部的消息队列里面取出一个消息,并回调相应的消息处理函数.一般在任务常驻,比如用户交互任务的情况下使用异步消息处理线程. 之前在Android中Handler原理里面研究过android里实现异步消息处理线程的方式,基本逻辑如图所示 今天就用java将其简单的模拟出来加深印象,下面的类图是用工具导出的,不太正规,不过能大概看出类之间的关系 Message类:消息类 public class Message { public int