使用handler时的warning:This Handler class should be static or leaks might occur

 

 
 

  在使用handler 的时候经常会出现这样一条警告:  This Handler class should be static or leaks might occur 。

  这条warning不会影响代码的编译和执行,但既然弹出了一条warning,肯定是有问题的,希望能把这个点给解开。

  从stackoverflow上找到的解答是这样的(【4】):

  

  If  MyHandler class is not static, it will have a reference to your Service/Activity object.

  Handler objects for the same thread all share a common Looper object, which they post messages to and read from.

  As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Service/Activity cannot be garbage collected, even after being destroyed.

  

  这条解答非常明白,下面我将把解答里的话逐条解释一下。可以找到handler警告的原因和解决方案。

1. If  MyHandler class is not static, it will have a reference to your Service/Activity object.

  涉及到了java中内部类与静态内部类的差别。这一部分的知识,我参考了文献【1】【2】。

  内部类有一个指向外部类的引用,而静态内部类没有指向外部类的引用。(这一点也导致了内部类和静态内部类使用范围上的差异!)更本质的原因是因为他们存放的方式不同。

  静态内部类的特点有:

    不可以使用外部类的非静态成员或者其他内部类成员。(原因是:静态内部类没有指向外部类的引用)
    如果创建静态内部类的对象,不需要其外部类的对象。(直接用外部类的类名即可)
    一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。

 一些背景知识:java的内存管理。通过了解java的内存管理才能分清静态成员和非静态成员的本质差异。

      (栈内存,堆内存,方法区)

        堆:堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

        栈:栈中存放一些基本类型的变量和对象句柄(其实也是int型)。

          堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址。栈中的这个变量就成了数组或对象的引用变量。

          引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

           方法区:在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来

    

2.  Handler objects for the same thread all share a common Looper object, which they post messages to and read from.

  这句话介绍了hanlder的使用背景,意思是,同一个线程中的handler类型的对象,在处理message消息时共享一个looper。关于looper和handler的关系可以参考我的上一篇博客

使用AChartEngine画图,项目总结中的第三部分。

  

3.  As messages contain target Handler, as long as there are messages with target handler in the message queue, the handler cannot be garbage collected. If handler is not static, your Service/Activity cannot be garbage collected, even after being destroyed.

  这句话想表达的意思,应该是:

  handler类型的对象,其生命周期是不可控的。

  如果用了myHandler.dispatchMessage(msg)函数,除非msg在looper中被传递给myHandler执行,否则handler一直存在。

  这样可能带来的一个后果是,msg还在looper中排队,而此时myhandler所对应的外部类应该被destroy回收内存,但由于handler是其内部类,占用了外部类的引用,使得外部类不能被回收,从而造成了内存泄漏。

 

  这样再读stackoverflow的解答方案,就可以归结成:

    内部类有一个指向外部类的引用,而静态内部类没有指向外部类的引用。handler类型的对象,其生命周期是不可控的。handler如果是普通内部类,由于其隐式的有一个指向外部类的引用,导致外部类在destroy后不能被GC回收,导致内存泄漏。

  用一句通俗的话讲,这个warning提醒的目的,是把handler和外部类解耦合,避免内存泄漏。

  问题被梳理清楚了,解决方案自然也通透,就是把handler声明为静态内部类。

  新的问题又出现了,静态内部类不可以使用外部类的非静态成员或者其他内部类成员!在一些特定的应用中,handler需要使用到外部类的非静态成员,怎么办呢?

  也有成熟的解决方案,使用WeakReference。WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收。参见【3】

  使用方法如下:

 1 private Handler mHandler = new MyHandler(this);
 2 private static class MyHandler extendsHandler{
 3     private final WeakReference<Activity> mActivity;
 4     public MyHandler(Activity activity) {
 5         mActivity = newWeakReference<Activity>(activity);
 6     }
 7     @Override
 8     public void handleMessage(Message msg) {
 9         System.out.println(msg);
10         if(mActivity.get() == null) {
11             return;
12         }
13     }
14 }

 

参考文献:

【1】  http://blog.sina.com.cn/s/blog_463b79ca01011onp.html

【2】  http://blog.csdn.net/ilibaba/article/details/3866537

【3】  http://wiseideal.iteye.com/blog/1469295

【4】    http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler 

 

 

posted on 2013-08-07 13:34  Leo Han  阅读(452)  评论(0编辑  收藏  举报