Android中由Handler和内部类引起的内存泄漏
原文地址:http://johnnyshieh.github.io/android/2015/09/03/android-handler-memory-leak/
在Android中我们经常用Handler来处理异步消息,把非主线程的操作结果放在主线程中更新UI。请思考一下下面的代码有没有问题:
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
虽然看起来好像没什么问题,但是这段代码可能引起内存泄露。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
但是什么东西泄漏了,内存泄漏怎么发生的我们还是不清楚。接下来我们慢慢分析泄漏的原因:
当一个Android应用启动时,框架会分配到一个Looper实例给应用的主线程。这个Looper的主要工作就是处理一个接着一个的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法的调用和按钮的点击等)都是放到消息中,然后加入到Looper要处理的消息队列中,由Looper依次处理。主线程的Looper的生命周期和应用的一样长。
当在主线程中初始化一个Handler时,它就会关联到Looper的消息队列。发送到消息队列的消息本身就持有Handler的引用,只有这样Looper在处理这个条消息的时候才能调用Handler#handleMessage(Message)处理消息。
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。而静态的内部类则不会。
上面的代码还是很难察觉哪里会导致内存泄漏,那么再看看下面这个例子:
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();
}
}
注意上面的匿名Runnable类也隐式地持有SampleActivity的引用,这也会阻止context被回收。当Activity是finished的时候,被延迟的消息在被处理之前依然会存活在主线程的消息队列中10分钟。这个消息持有Activity的Handler的引用,而Handler隐式地持有其外部类(SampleActivity)的引用。这些引用会一直保持直到消息被处理完成,而这会导致Activity无法被垃圾回收器回收并且泄漏应用的所有资源,因此就产生了内存泄漏。
要解决这个问题,在继承Handler的时候要么放在一个单独的类文件中,要么用静态内部类替代。因为静态内部类不会持有其外部类的隐式引用,所以不会产生contex泄漏。如果要在Handler中调用外部Activity的方法,可以持有Activity的弱引用。同样的道理,要解决匿名Runanable类引起的内存泄漏,只需要把它设为静态的成员属性(静态的匿名内部类不会持有其外部类的隐式引用)。
修改后的代码如下:
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();
}
}
原文链接:How to Leak a Context: Handlers & Inner Classes 作者:Alex Lockwood静态内部类和非静态内部类的区别是我们每一个Android开发人员应该掌握的。当内部类的实例的生命周期可能大于Activity的生命周期时,避免使用非静态的内部类。个人更倾向于用静态内部类或持有Activity的弱引用。
Android中由Handler和内部类引起的内存泄漏的更多相关文章
- Android中使用Handler造成内存泄露的分析和解决
什么是内存泄露?Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向 ...
- Android中使用Handler造成内存泄露
1.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...
- Android中的Handler的机制与用法详解
概述: 很多android初学者对android 中的handler不是很明白,其实Google参考了Windows的消息处理机制, 在Android系统中实现了一套类似的消息处理机制.在下面介绍ha ...
- android中的Handler和Runnable
最近在做一个项目,在网络请求时考虑用Handler进行处理,然后就研究了一下Handler和Runnable 首先在看一下java中的Runnable The Runnable interface s ...
- Android中利用Handler实现消息的分发机制(三)
在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...
- 转:Android中的Handler的机制与用法详解
注:Message类的用法: message的几个参数都可以携带数据,其中arg1与arg2可以携带int类型,what是用户自定义的int型,这样接受者可以了解这个消息的信息. 说明:使用Messa ...
- 深入源代码解析Android中的Handler,Message,MessageQueue,Looper
本文主要是对Handler和消息循环的实现原理进行源代码分析.假设不熟悉Handler能够參见博文< Android中Handler的使用>,里面对Android为何以引入Handler机 ...
- Android中的Handler及它所引出的Looper、MessageQueue、Message
0.引入 0.1.线程间通信的目的 首先,线程间通信要交流些什么呢? 解答这个问题要从为什么要有多线程开始,需要多线程的原因大概有这些 最早也最基本:有的任务需要大量的时间,但其实并不占用计算资源,比 ...
- Android中的Handler,以及用Handler延迟执行
项目中遇到一个情况,先生成文件再上传:但有时候发出指令后上传会不成功,需要再发一次指令方能上传. 猜想是由于文件还没生成就执行「上传」指令了.想到要延时.Android中单纯用currentThrea ...
随机推荐
- 【问题解决】访问jhipster-registry-master出现空白页
问题概述: 刚从github拉下来的jhipster-registry-master直接运行,访问http://localhost:8761会发现会空白页,但是网页的title显示正常,本文目的是解决 ...
- spring boot 使用logback日志系统的详细说明
springboot按照profile进行打印日志 log4j logback slf4j区别? 首先谈到日志,我们可能听过log4j logback slf4j这三个名词,那么它们之间的关系是怎么样 ...
- 2017ACM暑期多校联合训练 - Team 1 1003 HDU 6035 Colorful Tree (dfs)
题目链接 Problem Description There is a tree with n nodes, each of which has a type of color represented ...
- bzoj 1934最小割
比较显然的最小割的题,增加节点source,sink,对于所有选1的人我们可以(source,i,1),选0的人我们可以(i,sink,1),然后对于好朋友我们可以连接(i,j,1)(j,i,1),然 ...
- bootstrap入门项目备份
bootstrap入门项目备份 http://files.cnblogs.com/files/wordblog/bootstrap%E5%85%A5%E9%97%A8%E9%A1%B9%E7%9B%A ...
- MongoDB 3.4.2 配置 CentOS 6.5 远程连接
1.新建用户 db.createUser({user: 'test', pwd: 'myPassword', roles: [{role: 'readWrite', db: 'test_db'}]}) ...
- linux自动创建dev node
通过驱动模块的加载在/dev下创建设备文件,在驱动模块卸载时又自动的删除在/dev下创建的设备文件非常方便.而这个过程就是通过device_create()和device_destroy()内核函数完 ...
- Deep Learning基础--理解LSTM网络
循环神经网络(RNN) 人们的每次思考并不都是从零开始的.比如说你在阅读这篇文章时,你基于对前面的文字的理解来理解你目前阅读到的文字,而不是每读到一个文字时,都抛弃掉前面的思考,从头开始.你的记忆是有 ...
- cocos2d-x android 添加新场景报错: undefined reference to `vtable for XXX'
转载自 居家懒人 http://www.cnblogs.com/JD85/archive/2012/09/17/2688128.html 加入写了新场景SecondScene,结果在cpp文件里类名地 ...
- Linux中涉及到环境变量的文件
1. 系统级 (a) /etc/profile : 在用户登录操作系统时,定制用户环境的第一个文件,应用于登录的每一个用户 ==> 该文件一般调用/etc/bash.bashrc文件 (b)/e ...