在使用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. H5页面在IOS下不会自动播放音乐的坑

    document.addEventListener(‘DOMContentLoaded‘, function () { function audioAutoPlay() { var audio = d ...

  2. 虚拟机实现https网络设置

    现在很多网站使用的都是https协议,想在自己的电脑上实现下, 由于自己的电脑是win10,我总是觉得在windows上布置环境不如在linux上稳定,所以在电脑上安装了虚拟机,cento系统 . 上 ...

  3. tcp/ip --IP:网际协议

    1.概述      IP是TCP/IP协议族中最为核心的协议.所有的TCP,UDP,ICMP,IGMP数据都以IP数据报格式传输.      IP提供不可靠,无连接的数据报传送服务. 不可靠:它不能保 ...

  4. Android AIDL Service 跨进程传递复杂数据

    黑夜 黑夜给了我黑色的眼睛,我却用它寻找光明~ 传值方式 AIDL是同意跨进程传递值的,一般来说有三种方式: - 广播:这样的算是比較常见的一种方式了,传递小数据不错 - 文件:这个是保存到文件里.然 ...

  5. VS2015 解决方案 或者项目 卡 正在加载 的解决办法

    导致项目无法打开以及VS无法关闭. 解决方法: 1.关闭VS: 2.去C:\Users\<your users name>\AppData\Local\Microsoft\VisualSt ...

  6. nginx源码学习_源码结构

    nginx的优秀除了体现在程序结构以及代码风格上,nginx的源码组织也同样简洁明了,目录结构层次结构清晰,值得我们去学习.nginx的源码目录与nginx的模块化以及功能的划分是紧密结合,这也使得我 ...

  7. 680. Valid Palindrome II【easy】

    680. Valid Palindrome II[easy] Given a non-empty string s, you may delete at most one character. Jud ...

  8. python学习之lambda()

    中学时期,数学老师不时在口中说着:“拉姆达!λ...”.这里的“拉姆达”表示第十一个希腊字母. 而在python中,lambda表示匿名函数. 先来看看匿名函数 >>> f = la ...

  9. java - day10 - TetrominoTesting

    图形试验,主要 向上造型 package com.example; /** * Created by Administrator on 17-5-23. */ public class Tetromi ...

  10. strrev 字符串反转函数

    strrev (PHP 3, PHP 4, PHP 5) strrev -- Reverse a string Description string strrev ( string string ) ...