在使用Handler更新UI的时候。我是这样写的:

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO
}
}
}

看起来非常正常的,可是 Android Lint 却给出了警告:

This Handler class should be static or leaks might occur

意思是说:这个Handler 必须是static的。否则就会引发内存泄露。

事实上。对于这个问题,Android Framework 的project师 Romain Guy 早已经在Google论坛上做出过解释,而且给出了他的建议写法:

I wrote that debugging code because of a couple of memory leaks I

found in the Android codebase. Like you said, a Message has a

reference to the Handler which, when it’s inner and non-static, has a

reference to the outer this (an Activity for instance.) If the Message

lives in the queue for a long time, which happens fairly easily when

posting a delayed message for instance, you keep a reference to the

Activity and “leak” all the views and resources. It gets even worse

when you obtain a Message and don’t post it right away but keep it

somewhere (for instance in a static structure) for later use.

他的建议写法是:

class OuterClass {

  class InnerClass {
private final WeakReference<OuterClass> mTarget; InnerClass(OuterClass target) {
mTarget = new WeakReference<OuterClass>(target);
} void doSomething() {
OuterClass target = mTarget.get();
if (target != null) {
target.do();
}
}
}

以下,我们进一步解释一下:

1.Android App启动的时候,Android Framework 为主线程创建一个Looper对象。这个Looper对象将贯穿这个App的整个生命周期,它实现了一个消息队列(Message Queue),而且开启一个循环来处理Message对象。而Framework的主要事件都包括着内部Message对象,当这些事件被触发的时候,Message对象会被加到消息队列中运行。

2.当一个Handler被实例化时(如上面那样),它将和主线程Looper对象的消息队列相关联,被推到消息队列中的Message对象将持有一个Handler的引用以便于当Looper处理到这个Message的时候。Framework运行Handler的handleMessage(Message)方法。

3.在 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() { }
}, 60 * 10 * 1000); // Go back to the previous Activity.
finish();
}
}

当Activity被finish()掉。Message 将存在于消息队列中长达10分钟的时间才会被运行到。

这个Message持有一个对Handler的引用,Handler也会持有一个对于外部类(SampleActivity)的隐式引用,这些引用在Message被运行前将一直保持。这样会保证Activity的上下文不被垃圾回收机制回收,同一时候也会泄露应用程序的资源(views and resources)。

为解决问题,以下这段代码中的Handler则是一个静态匿名内部类。静态匿名内部类不会持有一个对外部类的隐式引用,因此Activity将不会被泄露。假设你须要在Handler中调用外部Activity的方法,就让Handler持有一个对Activity的WeakReference,这样就不会泄露Activity的上下文了,例如以下所看到的:

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, 60 * 10 * 1000); // Go back to the previous Activity.
finish();
}
}

总结:

在实际开发中。假设内部类的生命周期和Activity的生命周期不一致(比方上面那种,Activity finish()之后要等10分钟,内部类的实例才会运行),则在Activity中要避免使用非静态的内部类,这样的情况,就使用一个静态内部类。同一时候持有一个对Activity的WeakReference。

关于handler内存泄露的问题的更多相关文章

  1. 小题大做 | Handler内存泄露全面分析

    前言 嗨,大家好,问大家一个"简单"的问题: Handler内存泄露的原因是什么? 你会怎么答呢? 这是错误的回答 有的朋友看到这个题表示,就这?太简单了吧. "内部类持 ...

  2. handler内存泄露

    原因: 在Activity中新建一个Handler后,Handler执行计时操作,如果Activity销毁,Handler是不会主动销毁的,而且会占用Activity的空间,不使其回收,积累久了就会内 ...

  3. Android handler 内存泄露分析及解决方法

    1. 什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引 ...

  4. Android性能优化:手把手带你全面了解 内存泄露 & 解决方案

    . 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM ...

  5. Android 内存泄露总结(附内存检测工具)

    https://segmentfault.com/a/1190000006852540 主要是分三块: 静态储存区:编译时就分配好,在程序整个运行期间都存在.它主要存放静态数据和常量. 栈区:当方法执 ...

  6. Android 常见内存泄露 & 解决方案

    前言 内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃 (OOM) 等严重后果. 那什么情况下不能被 ...

  7. Android中使用Handler造成内存泄露的分析和解决

    什么是内存泄露?Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向 ...

  8. Android中使用Handler造成内存泄露

    1.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...

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

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

随机推荐

  1. jQuery 如何存储,获取和删除 Cookies

    jQuery.cookie = function(name, value, options) { if (typeof value != 'undefined') { options = option ...

  2. 使用caffe 的 python接口测试数据,选定GPU编号

    只需要在python脚本中添加两行代码: caffe.set_device(0) #使用第一块显卡 caffe.set_mode_gpu() #设为gpu模式 这样,就可以在默认显卡被占用(第一块显卡 ...

  3. webpack 环境搭建基础框架

    一.安装babel相关 1,安装依赖 cnpm i -D babel-core babel-loader babel-preset-env babel-preset-stage- babel-plug ...

  4. C语言 指向结构体变量的指针

    一个指向变量的指针表示的是占内存中起始位置 一个指向结构体的变量的指针表示的是这个结构体变量占内存中的起始位置,同样它也可以指向结构体变量数组 定义结构体变量的指针: //假设已有一个结构体名为Stu ...

  5. Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系

    Atitit.获取swing ui 按钮控件的id 与名字 与JPDA 调试体系 1. Swing Inspector是一个Java Swing/AWT用户界面分析和调试工具,功能与firebug类似 ...

  6. HDU5374 Tetris (2015年多校比赛第7场)大模拟

    思路: 先写好了几个函数.旋转,四种操作,推断能否够进行合并消除 题中有好几处要考虑的细节问题,如 自然下落究竟部时不进行合并的推断,而是当自然下落非法时才推断 假设消除一行,这一行上面的所以方块仅仅 ...

  7. C# Activator.CreateInstance 动态创建类的实例(一)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. java & c sharp 的关联

    第一.java是真正的与平台无关,c sharp不是,他只是口头上的与平台无关,最后,却要靠别人来实现非微软平台的类库. 第二.java中的类名.class 和c#的 typeof(类名)或者getT ...

  9. UIWebView加上safari风格前进后退按钮(转)

    今天在写程序内打开网页的功能,写工具条的时候发现系统图标里面竟然没有后退按钮,,由于我这个是静态库工程,不可能自己弄张图上去,不然使用本库的时候还得附上图片,经过一下午的搜索,终于找到个比较靠谱的,这 ...

  10. Windows Azure 系列-- Azure Redis Cache的配置和使用

    假设还没有配置Azure Power shell 能够參照这里进行配置:http://blog.csdn.net/lan_liang/article/details/46850221 打开Azure ...