在使用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. VS项目名称修改

    阅读数:11141 VS中新建一个项目,如果开发工作都接近尾声,客户来要求更换项目的名称,差不多要变更整个解决方案中项目名称,引用等等,这个工作量还是很大的.上网搜索解决方法,还实验了专门的修改项目名 ...

  2. UVA 11014 - Make a Crystal(容斥原理)

    UVA 11014 - Make a Crystal 题目链接 题意:给定一个NxNxN的正方体,求出最多能选几个整数点.使得随意两点PQ不会使PQO共线. 思路:利用容斥原理,设f(k)为点(x, ...

  3. ashx后门

    一.标准ASPX一句话木马 .NET平台下的一句话木马则百年不变,最常见的当属下面这句 <%@ Page Language=”Jscript”%><%eval(Request.Ite ...

  4. IOS设计模式浅析之抽象工厂模式(Abstract Factory)

    概述 在前面两章中,分别介绍了简单工厂模式和工厂方法模式,我们知道简单工厂模式的优点是去除了客户端与具体产品的依赖,缺点是违反了“开放-关闭原则”:工厂方法模式克服了简单工厂模式的缺点,将产品的创建工 ...

  5. fabric 自动化部署

    fabric 项目发布和运维的工作相当机械,频率还蛮高,导致时间浪费在敲大量重复的命令上. 修复bug什么的,测试,提交版本库(2分钟),ssh到测试环境pull部署(2分钟),rsync到线上机器A ...

  6. makefile编写---:= ?= += =的区别

    在Makefile中我们经常看到 = := ?= +=这几个赋值运算符,那么他们有什么区别呢?我们来做个简单的实验 新建一个Makefile,内容为:ifdef DEFINE_VRE    VRE = ...

  7. 浅谈javascript的this指向

    This的指向大致能够分为下面四类.我们分别举例说明 1. 作为对象的方法调用时.this指向该对象 var obj={     a:1,     getA:function(){        co ...

  8. CSS基础4——使用CSS格式化元素内容的文本

    CSS的文本属性用于控制文本的段落格式,如设置首行缩进.段落对齐方式.字间距.行间距等. 1.设置文本首行缩进:text-indent 可选属性值包含: 长度 / 百分比 2.设置文本对齐方式:tex ...

  9. try finally 执行顺序

    class Exc{ int a; int b; } public class Except { @SuppressWarnings("finally") static int c ...

  10. webservice 使用axis2实现

    Axis2 是Apache的:使用下载 :org.apache.axis2.eclipse.service.plugin_1.6.2.jar org.apache.axis2.eclipse.code ...