转载:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1922.html

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

public class SampleActivity extends Activity {
private Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// TODO
}
}
}

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

This Handler class should be static or leaks might occur

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

其实,对于这个问题,Android Framework 的工程师 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();
}
}
private MyHandler handler = null;
// 如果是Fragment界面的话
public static class MyHandler extends Handler {
WeakReference<CommentaryFragment> fragment = null;
public MyHandler(CommentaryFragment fragment) {
this.fragment = new WeakReference<CommentaryFragment>(fragment);
}
public void handleMessage(Message msg) {
CommentaryFragment fr = fragment.get();
if(fr == null)
return;
switch (msg.arg1) {
case Constant.CONTANT_1:
fr.getBondingDataFriend(msg);
break;
}
};
};
@Override
public void onCreate(Bundle savedInstanceState) {
handler = new MyHandler(this);
super.onCreate(savedInstanceState);
}

当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。

85、android handler的警告Handler Class Should be Static or Leaks Occur的更多相关文章

  1. Handler Should be static or leaks Occur?

    解决办法: public class SampleActivity extends Activity { /** * Instances of static inner classes do not ...

  2. Android下setLatestEventInfo警告、Handler警告、SimpleDateFormat警告

    正 文: 今天飘易在做Android 4.4.2下的APP开发时,使用了Notification下的setLatestEventInfo()方法时,Eclipse却提示:“ 不建议使用类型 Notif ...

  3. Android“This Handler class should be static or leaks might occur”警告的处理方法

    此文属于转载! 最近用到handle在线程中改变UI,会跟给出“This Handler class should be static or leaks might occur”的警告,网上看了很多解 ...

  4. android之lint警告This Handler class should be static or leaks might occur

    更新到adt2.0的开发者们可能会在handler上发现这么一条警告:This Handler class should be static or leaks might occur . 首先在ADT ...

  5. Android App 内存泄漏Handler

    Android App 内存泄露之Handler Handler也是造成内存泄露的一个重要的源头,主要Handler属于TLS(Thread Local Storage)变量,生命周期和Activit ...

  6. Android线程和线程Handler基础一览

    线程概览 线程是任何多任务系统的基石.可以被认为是一个主进程的多个子进程.这样做的目的就是了增加应用的性能. 应用主线程 当一个Android应用被打开的时候,系统会默认开辟一个线程.这个线程就被叫做 ...

  7. Android消息机制探索(Handler,Looper,Message,MessageQueue)

    概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...

  8. Android之消息机制Handler,Looper,Message解析

    PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...

  9. Android Handler机制(四)---Handler源码解析

    Handler的主要用途有两个:(1).在将来的某个时刻执行消息或一个runnable,(2)把消息发送到消息队列. 主要依靠post(Runnable).postAtTime(Runnable, l ...

随机推荐

  1. TFS2013以独占的方式签出

  2. winform通过网络获取用户信息

    1.获取当前部署: public static NameValueCollection GetQueryStringParameters() { NameValueCollection nameVal ...

  3. Java Persistence with MyBatis 小结1

    数据持久层做的工作是1)将从数据库中查询到的数据生成需要的java对象:2)将 Java 对象中的数据通过 SQL 持久化到数据库中. MyBatis 通过抽象底层的 JDBC 代码,自动化 SQL ...

  4. Experience on Namenode backup and restore --- checkpoint

    Hadoop version: Hadoop 2.2.0.2.0.6.0-0009 Well, We can do this by building Secondary Namenode, Check ...

  5. bazel-编译多目标

    demo2 使用bazel编译多目标示例,一个bianry,一个library. demo2目录树 ── demo2 ​ ├── app ​ │ ├── BUILD ​ │ ├── func.cpp ...

  6. Python正则表达式中的re.S的作用

    在Python的正则表达式中,有一个参数为re.S.它表示“.”(不包含外侧双引号,下同)的作用扩展到整个字符串,包括“\n”.看如下代码: import re a = '''asdfhellopas ...

  7. CSS 命名规范总结

    CSS 命名规范总结 BEM 命名给 CSS 以及 html 提供清晰结构,命名空间提供更多信息,模块化提高代码的重用,以达到 CSS 命名语义化.可重用性高.后期维护容易.加载渲染快的要求. 首先, ...

  8. 我的《C陷阱与缺陷》读书笔记

    第一章 词法“陷阱” 1. =不同于== if(x = y) break; 实际上是将y赋给x,再检查x是否为0. 如果真的是这样预期,那么应该改为: if((x = y) != 0) break; ...

  9. lzma解压

    这个软件的使用方法有点特殊:需要将要压缩为lzma格式的文件拖放到批处理上面,会自动进行处理.压缩和解压同样是拖放到上面,程序会自动处理.程序默认使用2个CPU线程进行处理,会自动判断你是压缩还是解压 ...

  10. jquery 怎么触发select的change事件

    可以使用jQuery的trigger() 方法来响应事件 定义和用法 trigger() 方法触发被选元素的指定事件类型. 语法 $(selector).trigger(event,[param1,p ...