内存泄漏之内部类new Handler();
来源:互联网 发布:数据中心网络拓扑图 编辑:程序博客网 时间:2024/06/07 03:20
首先给同学们回忆一下:
非静态的内部内的生成对象是:new Outer().new Inner(); 静态内部类的生成对象的过程是 Outer.new Inner();或new Outer.Inner();总之外部类不用实例化;
错误代码
如果在Activiy中通过内部类(Runnable)的方式定义了一个变量runnable,
final Runnable runnable = new Runnable() {
public void run() {
// … do some work
}
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10)
因为Runnable不是static类型,所以会有一个包含Activity实例的implicit reference — Activity.this。
如果Activity在runnable变量run之前(10s内)被finish掉了但是Activity.this仍然存在,那么Activity的对象就不会被GC回收,从而导致memory leak。
即使使用一个静态内部类,也不能保证万事大吉。
static class MyRunnable implements Runnable {
private View view;
public MyRunnable(View view) {
this.view = view;
}
public void run() {
// … do something with the view
}
}
假设在runnable执行之前,View被移除了,但是成员变量view还在继续引用它,仍然会导致memory leak。
上面的两个例子当中,导致内存泄露的两种用法分别是隐式引用(implicit reference) 和 显式引用(explicit reference)。
http://img0.tuicool.com/VJFf2au.jpg!web
为什么发生内存泄露?
由上文可以看出:当mHandler没有被回收时,其外围Activity对象不能被回收。当Activity被用户关闭(finish),而此时mHandler还未被回收,那么Activity对象就不会被回收,造成Activity内存泄露。
问题的关键转入到了这个问题:为什么Activity被finish了,mHandler还不能被回收?
发送消息时,我们使用了这个函数:mHandler.sendEmptyMessage(0)函数。通过查看源码追踪调用关系,发现走到了:
Message对象有个target字段,该字段是Handler类型,引用了当前Handler对象。一句话就是:你通过Handler发往消息队列的Message对象持有了Handler对象的引用。假如Message对象一直在消息队列中未被处理释放掉,你的Handler对象就不会被释放,进而你的Activity也不会被释放。
解决方法
方案#1:在关闭Activity时(finish/onStop等函数中),取消还在排队的Message:mHandler.removeCallbacksAndMessages(null);
解决隐式引用的方法比较简单,只要使用内部非静态类(non-static inner class)或者 top-level class(在一个独立的java文件中定义的变量)就可以将隐式变为显式,从而避免内存泄露。
如果继续使用非静态内部类,那么就要在onPause的时候手动结束那些挂起的任务(pending task)。
关于如何结束任何,Handler可参考这篇文章中的Canceling a pending Runnable和Canceling pending Messages。HandlerThread可参考这篇文章。
解决第二个问题要用到WeakReference,WeakReference的用法可以google一下,简而言之就是:只要还有其他的stronger reference,WeakReference就可以继续引用。
static class MyRunnable implements Runnable {
private WeakReference>View< view;
public MyRunnable(View view) {
this.view = new WeakReference>View<(view);
}
public void run() {
View v = view.get();
if (v != null) {
// … do something with the view
}
}
}
这样一来问题就解决了,美中不足的是每次使用view之前都要做空指针判断。另外一个比较高效的方法就是在onResume中为runnable的view赋值,在onPause中赋值为null。
private static class MyHandler extends Handler {
private TextView view;
public void attach(TextView view) {
this.view = view;
}
public void detach() {
view = null;
}
@Override
public void handleMessage(Message msg) {
// ….
}
总结
在继承Handler或者HandlerThread的时候,
尽量定义一个static类或者top-level类。如果用到了ui元素,一定要在Activity的生命周期接触之前释放掉。
- 内存泄漏之内部类new Handler();
- 内存泄漏之内部类handler()
- 内部类与内存泄漏(Handler,Runnable)
- 自定义Handler使用静态内部类避免内存泄漏
- 自定义无内存泄漏的Handler内部类
- 内部类Handler引发的内存泄漏问题
- 怎么使Handler净身出户(非静态内部类和匿名内部类造成内存泄漏)
- Handler可能造成内存泄漏(四)
- handler内存泄漏
- 防止handler内存泄漏
- Handler内存泄漏
- Handler内存泄漏
- Android Handler 内存泄漏
- Handler 内存泄漏问题
- Android Handler内存泄漏
- Handler内存泄漏
- Handler导致内存泄漏
- Handler内存泄漏分析
- 9. Palindrome Number
- leetcode:word-break
- ReactNative Demo - Navigator 使用
- mongoDB 入门指南、示例
- 2016/07/20 对闭包、构造函数的理解
- 内存泄漏之内部类new Handler();
- UVA 846-Steps
- ReactNative Demo -SearchInput
- 社会化三方分享集成详细介绍(友盟)
- Ajax开发步骤解析
- hive变量传递
- NYOJ35组合数
- Java中对于unsigned byte类型的转换处理
- 再一篇centos下的 rpm 与yum 安装方法 (详解)