欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler leaks

程序员文章站 2022-07-14 18:29:54
...

在使用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 的工程师 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 leaks