Context泄漏:Handlers & Inner Classes

来源:互联网 发布:淘宝运费险保费 编辑:程序博客网 时间:2024/06/13 09:13



先思考下面一个代码片段

publicclass SampleActivity extends Activity {   private final Handler mLeakyHandler = new Handler() {    @Override    publicvoid handleMessage(Message msg) {      // ...    }  }}

虽然不容易发现,但上面的代码会导致大量的内存泄漏。如果是在Eclipse中编写代码的话Android Lint会警告:In Android,Handlers should be static or leaks might occur。但是内存泄漏到底是在哪发生的和到底是怎么发生的?现在就让我们来确定问题的根源,首先我们知道的是:

     1、当一个Android应用启动时,Android平台框架会为应用的UI主线程创建一个Looper对象。Looper简单的实现了一个消息队列,采用队列的方式处理一个个消息。所有主要的应用程序框架中的事件都包含在消息对象中,所有这些事件消息都加入到Looper的消息队列中,一个接着一个的处理掉。并且UI主线程的Looper对象贯穿于应用程序的整个生命周期。

     2、当一个Handler对象在UI主线程中经过实例化后,就与Looper消息队列相关联了。当一个消息发布进消息队列中时,Looper对象会持有一个Handler对象,以便于当Loope处理此消息时,Android平台框架能够调用Handler对象的 Handler#handleMessage(Message) 方法来进行处理 

     3、在Java当中,非静态的和匿名的内部类会隐式的持有它们外部类的一个引用

那么到底在哪里内存泄漏了呢?虽然非常的隐秘微妙,但是咋们不妨来看看下面一段代码

publicclass SampleActivity extends Activity {   privatefinal Handler mLeakyHandler = new Handler() {    @Override    publicvoid handleMessage(Message msg) {      // ...    }  }   @Override  protectedvoid onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);     // Post a message and delay its execution for 10 minutes.    mLeakyHandler.postDelayed(newRunnable() {      @Override      publicvoid run() { }    },60 * 10 * 1000);         // Go back to the previous Activity.    finish();  }}

当这个Activity结束销毁时,推迟的处理的消息依然会存在于消息队列中10分钟直到被处理掉。消息对象持有Activity的Handler对象的引用,而Handler对象持有一个隐式的外部类的引用(在上面的例子中是SampleActivity)。这个Activity引用会一直存在直到该消息被处理掉,因此阻止了垃圾回收器对Activity Context对象的回收,泄漏了所有的应用程序资源。上面的代码片中的第15行,非静态Runnable匿名类隐式持有Activity一个引用,而非静态匿名类对象Handler又隐式的持有Activity引用,所以Activity Context对象泄露了。

要解决此问题,我们可以使用一个外部的Handler子类(Activity Context依然可能会被泄漏,稍后会提到规避的方法)或者使用一个静态的Handler子类内部类。静态内部类并不会隐式的持有外部类的一个引用,所以不会泄漏Activity Context对象。但是如果你希望在静态内部类Handler子类中使用外部类Activity中的一些属性或方法,我们可以在静态内部类中持有一个外部类Activity的弱引用,这样Activity Context对象就不会泄漏了。当然我们也要把Runnable定义成一个静态的属性(静态的匿名类对象不会持有外部类的引用),如下代码所示

publicclass SampleActivity extends Activity {   /**   * Instances of static inner classes do not hold an implicit   * reference to their outer class.   */  privatestatic class MyHandler extends Handler {    privatefinal WeakReference<SampleActivity> mActivity;     publicMyHandler(SampleActivity activity) {      mActivity = new WeakReference<SampleActivity>(activity);    }     @Override    publicvoid handleMessage(Message msg) {      SampleActivity activity = mActivity.get();      if(activity != null) {        // ...      }    }  }   privatefinal MyHandler mHandler = new MyHandler(this);   /**   * Instances of anonymous classes do not hold an implicit   * reference to their outer class when they are "static".   */  privatestatic final Runnable sRunnable = new Runnable() {      @Override      publicvoid run() { }  };   @Override  protectedvoid onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);     // Post a message and delay its execution for 10 minutes.    mHandler.postDelayed(sRunnable,600000);         // Go back to the previous Activity.    finish();  }}

虽然静态和非静态内部类之间的区别是相当微小的,但是每个Android开发者都应当知道。我们应当尽量避免在Activity中使用生命周期长于Activity生命周期的内部类,而应当使用静态内部类并且持有Activity的一个弱引用方式。