`
gg163
  • 浏览: 6944 次
文章分类
社区版块
存档分类
最新评论

Android 中 Handler 引起的内存泄露

 
阅读更多

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

1.public class SampleActivity extends Activity { 

2. 

3.    private final Handler mLeakyHandler = new Handler() { 

4.      @Override 

5.      public void handleMessage(Message msg) { 

6.        // ...  

7.     } 

8.    } 

9. 

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

In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application threads 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

看到这里,可能还是有一些搞不清楚,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?由移动应用安全团队-爱内测(www.ineice.com)的技术工程师为我们分析下:

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

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

3.Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。确实上面的代码示例有点难以察觉内存泄露,那么下面的例子就非常明显了

1. public 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.

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

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

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

1.  public class SampleActivity extends Activity { 

2.   

3.    /** 

4.     * Instances of static inner classes do not hold an implicit 

5.    * reference to their outer class. 

6.     */ 

7.    private static class MyHandler extends Handler { 

8.      private final WeakReference<SampleActivity> mActivity; 

9.   

10.    public MyHandler(SampleActivity activity) { 

11.      mActivity = new WeakReference<SampleActivity>(activity); 

12.    } 

13. 

14.    @Override 

15.   public void handleMessage(Message msg) { 

16.      SampleActivity activity = mActivity.get(); 

17.      if (activity != null) { 

18.        // ... 

19.      } 

20.    } 

21.  } 

22. 

23.  private final MyHandler mHandler = new MyHandler(this); 

24. 

25.  /** 

26.   * Instances of anonymous classes do not hold an implicit 

27.   * reference to their outer class when they are "static". 

28.   */ 

29.  private static final Runnable sRunnable = new Runnable() { 

30.      @Override 

31.      public void run() { /* ... */ } 

32.  }; 

33. 

34.  @Override 

35.  protected void onCreate(Bundle savedInstanceState) { 

36.    super.onCreate(savedInstanceState); 

37. 

38.    // Post a message and delay its execution for 10 minutes. 

39.    mHandler.postDelayed(sRunnable, 1000 * 60 * 10); 

40. 

41.    // Go back to the previous Activity. 

42.    finish(); 

43.  } 

44.

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

 

 

0
1
分享到:
评论

相关推荐

    Android中Handler引起的内存泄露问题解决办法

    在本文中,我们将深入探讨一个特定的内存泄露场景:由Handler引起的内存泄露,以及如何解决这个问题。 首先,我们需要理解Handler在Android中的作用。Handler是Android异步消息处理机制的关键组件,它与Looper和...

    Android 中Handler引起的内存泄露

    本文将深入探讨Android中Handler引起的内存泄露问题,以及如何避免这些问题。 1. **Handler与内存泄露的关系** 当我们创建一个Handler实例时,它会与当前线程的Looper关联。对于主线程,这个Looper是与应用程序的...

    android内存泄露测试

    内存泄露可能由多种原因引起,包括但不限于对象引用未正确管理、静态集合的过度使用、Handler和Runnable的错误处理等。在Android开发中,主要依靠以下工具进行内存泄露检测: - **MAT(Memory Analyzer Tool)**:...

    Android内存优化——常见内存泄露及优化方案

    1. 使用弱引用(WeakReference/SoftReference):对于可能引起内存泄露的对象,可考虑使用弱引用或软引用,以便在内存不足时自动回收。 2. 避免在静态变量或单例中持有Context:尽量使用Application Context,而不是...

    Android内存泄漏解决方案

    本文将详细介绍如何利用Eclipse Memory Analyzer Tool (MAT) 插件来检测和分析Android应用中的内存泄漏。 #### 二、准备工作 **1. 安装Eclipse MAT插件** - **下载地址**:...

    Android开发最常见的5大内存泄漏.docx

    3. Handler引起的内存泄漏: 当Handler与Activity关联时,如果Handler中的消息队列中有未处理的消息,即使Activity已经销毁,Handler仍然会保持对Activity的引用,导致内存泄漏。解决方法是在Activity的onDestroy()...

    Android 内存泄露

    5. Handler引起的内存泄漏:Handler与Message之间的引用可能导致非静态Handler实例的Activity或Service无法被回收,尤其是Message未被及时处理时。 预防内存泄漏的方法: 1. 避免长时间持有Activity引用,确保引用...

    Android 内存泄漏调试经验分享

    #### 二、Android(Java)中常见的容易引起内存泄漏的不良代码 ##### (一) 查询数据库没有关闭游标 当在Android中执行SQL查询操作时,如果没有正确关闭游标(`Cursor`),则可能导致内存泄漏。这是因为`Cursor`对象...

    Android开发最常见的5大内存泄漏

    在Android应用开发中,内存泄漏是一个非常重要的问题,它会导致应用程序占用过多的内存,从而影响性能,甚至引发应用崩溃。本文将深入探讨Android开发中最常见的五种内存泄漏情况,并提供相应的解决方案。 一、静态...

    Android_内存泄漏研究及调试.doc )

    #### 二、Android (Java) 中常见的容易引起内存泄漏的不良代码 在Android应用开发中,不当的内存管理可能导致内存泄漏。下面列举了一些常见的内存使用不当的情况: ##### (一) 查询数据库没有关闭游标 **描述**:...

    5个Android开发中比较常见的内存泄漏问题及解决办法

    3. **Handler引起的内存泄漏** Handler通常用于异步消息处理,但如果在Activity中创建了一个Handler,并且Handler的Looper与主线程的Looper关联,那么Handler会持有Activity的引用,导致Activity无法被垃圾回收。...

    Android防止内存溢出浅析

    - 使用WeakReference或SoftReference来持有对Activity、Context或其他易引起内存泄露的对象的引用。 - 避免在静态变量中存储非静态数据,特别是与UI相关的对象。 通过以上策略,开发者可以更有效地管理Android应用...

    Android中的内存泄漏

    内存泄漏在Android开发中是一个非常重要的概念,它指的是程序中不再使用的对象因未正确释放而持续占用内存资源,导致系统可用内存减少,进而影响程序性能甚至导致程序崩溃。内存泄漏通常发生在长生命周期的对象持有...

    初中级Android开发社招面试之Handler.pdf

    【Android开发中的Handler消息机制详解】 Handler作为Android开发中的核心组件之一,主要用于...在实际开发中,应特别注意防止因Handler引起的内存泄露,以及遵循正确的线程访问规则,以保证应用的稳定性和用户体验。

    Android应用源码之防止内存溢出浅析.zip

    3. **Activity生命周期**:Activity是Android应用的核心组件,其生命周期管理不当也会引起内存问题。例如,如果在onCreate()方法中创建大量对象,而没有在onDestroy()中正确清理,可能会导致Activity被回收后,内存...

    内存泄露从入门到精通三部曲之常见原因与用户实践1

    3. 使用WeakReference或SoftReference管理可能引起内存泄露的对象。 4. 注意线程的生命周期管理,避免长时间运行的任务。 5. 对于回调接口,添加和移除操作要平衡,避免只add不remove。 6. 在退出页面或组件时,确保...

    初中级Android开发社招面试之Handler.zip

    - 如何避免Handler引起的内存泄漏? - 请解释Message、Handler和Looper之间的关系? - 在子线程中如何更新UI? 通过深入理解并熟练运用Handler机制,你可以更好地解决Android应用中的异步问题,同时在面试中展现...

Global site tag (gtag.js) - Google Analytics