内存泄漏之内部类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的生命周期接触之前释放掉。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 好哭的孩子老师怎么办 小孩哭的犟住怎么办 2-3小孩脾气很犟怎么办 生了孩子没奶怎么办 二年级孩子写作业慢怎么办 六年级下册数学解决问题差的怎么办 孩子五年级了学习成绩不好怎么办 孩子做错事不承认怎么办 二年级小孩拼音差怎么办 小学三年级数学60多分怎么办 孩子三年级了数学不开窍怎么办 初二数学太差该怎么办 孩子语文阅读理解能力差怎么办 5年级数学不开窍怎么办 一年级孩子数学不开窍怎么办 三年级孩子数学成绩差怎么办 孩子三年级数学太差怎么办 三年级孩子数学理解能力差怎么办 教小孩做作业老是发火怎么办 孩子拿笔重写字太黑怎么办 教小孩写不到汉字怎么办 一年级孩子数学理解能力差怎么办 小孩一年级数学学不好怎么办 孩子一年级数学学不好怎么办 小学一年级数学学不好怎么办 孩子一年级数学成绩不好怎么办 成绩差的孩子该怎么办 面对成绩差的孩子该怎么办 六年级数学成绩差该怎么办 孩子数学物理成绩差该怎么办 高三成绩很差该怎么办 孩子学习成绩差家长该怎么办 孩子一年级语文成绩不好怎么办 孩子上网成瘾不听父母话怎么办 10岁数学不开窍怎么办 孩子五年级数学不好怎么办 一岁宝宝难断奶怎么办 2岁宝宝断不了奶怎么办 快2岁宝宝不听话怎么办 2岁半的宝宝不听话怎么办 3岁宝宝哭闹不止怎么办