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 void handleMessage(Message msg) {
// ...
}
}
}

While not readily obvious, this code can cause cause a massive memory leak. Android Lint will give the following warning:

In Android, Handler classes should be static or leaks might occur.

But where exactly is the leak and how might it happen? Let's determine the source of the problem by first documenting what we know:

  1. When an Android application first starts, the framework creates a Looper object for the application's main thread. ALooper implements a simple message queue, processing Message objects in a loop one after another. All major application framework events (such as Activity lifecycle method calls, button clicks, etc.) are contained insideMessage objects, which are added to the Looper's message queue and are processed one-by-one. The main thread'sLooper exists throughout the application's lifecycle.

  2. When a Handler is instantiated on the main thread, it is associated with the Looper's message queue. Messages posted to the message queue will hold a reference to the Handler so that the framework can callHandler#handleMessage(Message) when the Looper eventually processes the message.

  3. In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.

So where exactly is the memory leak? It's very subtle, but consider the following code as an example:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
}
}

When the activity is finished, the delayed message will continue to live in the main thread's message queue for 10 minutes before it is processed. The message holds a reference to the activity's Handler, and the Handler holds an implicit reference to its outer class (the SampleActivity, in this case). This reference will persist until the message is processed, thus preventing the activity context from being garbage collected and leaking all of the application's resources. Note that the same is true with the anonymous Runnable class on line 15. Non-static instances of anonymous classes hold an implicit reference to their outer class, so the context will be leaked.

To fix the problem, subclass the Handler in a new file or use a static inner class instead. Static inner classes do not hold an implicit reference to their outer class, so the activity will not be leaked. If you need to invoke the outer activity's methods from within the Handler, have the Handler hold a WeakReference to the activity so you don't accidentally leak a context. To fix the memory leak that occurs when we instantiate the anonymous Runnable class, we make the variable a static field of the class (since static instances of anonymous classes do not hold an implicit reference to their outer class):

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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();
}
}

The difference between static and non-static inner classes is subtle, but is something every Android developer should understand. What's the bottom line? Avoid using non-static inner classes in an activity if instances of the inner class could outlive the activity's lifecycle. Instead, prefer static inner classes and hold a weak reference to the activity inside.

Android中Handler导致的内存泄露的更多相关文章

  1. Android 中 Handler 引起的内存泄露

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.其实这可能导致内存泄露,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下.http://w ...

  2. Android中Handler引起的内存泄露

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. 1 2 3 4 5 6 7 8 9 public class SampleActivit ...

  3. Android handler 可能会造成内存泄露

    Android handler 可能会造成内存泄露 Android Studio 使用 Handler 时: private Handler handler = new Handler(){ @Ove ...

  4. android:布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  5. Android性能优化——之防止内存泄露

    又是好久没有写博客了,一直都比较忙,最近终于有时间沉淀和整理一下最近学到和解决的一些问题. 最近进行技术支持的时候,遇到了几个崩溃的问题,都是OOM异常,一般OOM异常给人的感觉应该是加载大图片造成的 ...

  6. dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露

    本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...

  7. Android中Handler 、Thread和Runnable之间的关系

    在多线程编程的时候,我们经常会用到Handler,Thread和Runnable这三个类,我们来看看这三个类之间是怎么样的关系? 首先说明Android的CPU分配的最小单元是线程,Handler一般 ...

  8. 关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析

    原文:关于Android中图片大小.内存占用与drawable文件夹关系的研究与分析 相关: Android drawable微技巧,你所不知道的drawable的那些细节 经常会有朋友问我这个问题: ...

  9. JS中由闭包引发内存泄露的深思

    目录 一个存在内存泄露的闭包实例 什么是内存泄露 JS的垃圾回收机制 什么是闭包 什么原因导致了内存泄露 参考 1.一个存在内存泄露的闭包实例 var theThing = null; var rep ...

随机推荐

  1. onethink框架显示Access denied for user 'root'@'localhost' (using password: NO)

    本地开发的时候使用的用户名是root,密码为空,它会生成两份.一份在Common/config.php里面,还有一份在Application\User\Conf/config.php 在linux环境 ...

  2. 20162328蔡文琛week01

    学号20162328 <程序设计与数据结构>第1周学习总结 教材学习内容总结 通过练习课本上给出的代码并结合老师所提供教程,熟悉并初步了解Java的基本编辑 教材学习中的问题和解决过程 无 ...

  3. java---Map接口实现类

    Map是一个双列集合接口,如果实现了Map接口,特点是数据以键值对形式存在,键不可重复,值可以重复.java中主要有HashMap.TreeMap.Hashtable.本文主要介绍Map的接口方法: ...

  4. arm交叉编译器gnueabi、none-eabi、arm-eabi、gnueabihf的区别

    转自 https://www.cnblogs.com/linuxbo/p/4297680.html 命名规则 交叉编译工具链的命名规则为:arch [-vendor] [-os] [-(gnu)eab ...

  5. URL中编码问题

    1.http协议传输统一iso-8859-1传输 jsp中用request.getparameter("keword");得到的是iso-8859-1翻译过来的,要用 keywor ...

  6. 用c++读取文件夹中的所有文件名

    //头文件,注意要加stdafx.h和io.h等 #include "stdafx.h" #include <io.h> #include <vector> ...

  7. VM新安装centos7无法连接网络的问题

    https://blog.csdn.net/u012110719/article/details/42264601 https://blog.csdn.net/kexiaoling/article/d ...

  8. 深入学习 Redis系列

    深入学习 Redis(1):Redis 内存模型 深入学习 Redis(2):持久化 深入学习 Redis(3):主从复制 深入学习 Redis(4):哨兵

  9. Lucene笔记一

    Lucene就是一个全文检索的工具,建立索引用的,类似于新华字典的目录 这里使用的是lucene-4.4.0版本,入门代码所需jar包如下图所示(解压lucene-4.4.0后的目录): 入门代码: ...

  10. codeforces 985 E. Pencils and Boxes (dp 树状数组)

    E. Pencils and Boxes time limit per test 2 seconds memory limit per test 256 megabytes input standar ...