Handler作为Android中一个消息传递的工具,使用非常频繁。不论是应用层开发,还是系统库件如AsyncTask的封装,都或多或少地使用了它。然而,Handler的危险性也是非常大的,使用起来稍有不慎就会引起内存泄露。
泄露来源分析:
常见错误用法:
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() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
这个用法的错误在于,它创造了一条非常长的引用链。Handler作为Activity的内部类,会自动持有Activity的引用,然后Handler通过postDelay发出一个Message,等待着它的回音,因为Message也会持有Handler的引用。Message会被放入MessageQueue中,等待被执行。归纳一下,我们可以看到以上的代码实现了如下一条引用链:
MessageQueue -----> Message -----> Handler -----> Activity
这条引用链非常长,并且MessageQueue的生命周期是Application级的。如果这条引用链不断,里面的任何一个对象,都将无法被回收。
解决方法:
在此提供两种解决思路
一、弱化Handler -----> 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, 1000 * 60 * 10); // Go back to the previous Activity. finish(); } }
这个思路的核心即是通过静态类和弱引用,来弱化Handler -----> Activity。Handler变成静态类,这样它就只属于Activity的类,不属于Activity的对象。然后使Handler中以弱引用握持Activity,这样两个类之间的联系就可以弱化,Handle即使被Message引用,Activity也可以照常回收。
这个思路的好处就是方法比较明确,比较稳定。并且在Activity的情况下,也不会有什么问题。但是如果我的Handle是存在于一个View。而每个View都需要一个Handler来控制它,此时用static类就是不适合的。那么我们只能另寻办法。
二、切断MessageQueue -----> Message:
MessageQueue -----> Message -----> Handler -----> Activity
我们可以从MessageQueue -----> Message下手,考虑切断它们的引用联系。
所幸Handler为我们提供了方法
/** * Remove any pending posts of callbacks and sent messages whose * <var>obj</var> is <var>token</var>. If <var>token</var> is null, * all callbacks and messages will be removed. */ public final void removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token); }
使用这个方法,我们可以将此Handler在MessageQueue中的所有Message清除。同时,我们还要将Activity中所有的Runnable停止。也就是说,现在是我们需要手动清除Activity的所有强引用。这样也可以防止内存泄露。这个方法对结构的破怀性小,好处显而易见。但坏处也同样明显,如果我们在各种引用中漏掉了某一项没有清除,那前面的所有动作都前功尽弃了。因此,这个方法难度比较大,难以保证其正确性。
理解有限,欢迎拍砖。
Done