[关闭]
@linux1s1s 2017-01-22T16:03:26.000000Z 字数 2336 阅读 3200

Android 内存泄露 (一)

AndroidMemory 2015-04


潜藏隐患的Handler

考虑到下面的代码:

  1. public class SampleActivity extends Activity {
  2. private final Handler mLeakyHandler = new Handler() {
  3. @Override
  4. public void handleMessage(Message msg) {
  5. // ...
  6. }
  7. }
  8. }

上面这段代码,虽然不是很明显,但是会导致内存泄露,Android Studio会做出下面的提示

In Android, Handler classes should be static or leaks might occur.

为神马会导致内存泄露呢?它又是如何发生的呢?
首先先来梳理一下我们已知的部分知识:

因此为神马会内存泄露?这个比较微妙,为了清晰起见,看下面的代码:

放大隐患的Handler

  1. public class SampleActivity extends Activity {
  2. private final Handler mLeakyHandler = new Handler() {
  3. @Override
  4. public void handleMessage(Message msg) {
  5. // ...
  6. }
  7. }
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. // Post a message and delay its execution for 10 minutes.
  12. mLeakyHandler.postDelayed(new Runnable() {
  13. @Override
  14. public void run() { /* ... */ }
  15. }, 1000 * 60 * 10);
  16. // Go back to the previous Activity.
  17. finish();
  18. }
  19. }

当SampleActivity生命周期结束的时候,延迟的Message依然会存活10分钟,直到它处理了这个消息,而这个Message持有Handler的引用,而Handler持有外部类SampleActivity的引用,所以GC不会回收SampleActivity,即使它已经生命周期结束很久了,这样便导致了内存泄露。另外注意到弟15行仍然有个匿名内部类Runnable的引用最终也会导致同样内存泄露,那么如何优化上面的代码呢?我们可以尽量采用静态内部类和非匿名类,尤其是非静态内部类或者匿名类具有比外部类更长的生命周期的情况

优化的Handler

  1. public class SampleActivity extends Activity {
  2. /**
  3. * Instances of static inner classes do not hold an implicit
  4. * reference to their outer class.
  5. */
  6. private static class MyHandler extends Handler {
  7. private final WeakReference<SampleActivity> mActivity;
  8. public MyHandler(SampleActivity activity) {
  9. mActivity = new WeakReference<SampleActivity>(activity);
  10. }
  11. @Override
  12. public void handleMessage(Message msg) {
  13. SampleActivity activity = mActivity.get();
  14. if (activity != null) {
  15. // ...
  16. }
  17. }
  18. }
  19. private final MyHandler mHandler = new MyHandler(this);
  20. /**
  21. * Instances of anonymous classes do not hold an implicit
  22. * reference to their outer class when they are "static".
  23. */
  24. private static final Runnable sRunnable = new Runnable() {
  25. @Override
  26. public void run() { /* ... */ }
  27. };
  28. @Override
  29. protected void onCreate(Bundle savedInstanceState) {
  30. super.onCreate(savedInstanceState);
  31. // Post a message and delay its execution for 10 minutes.
  32. mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
  33. // Go back to the previous Activity.
  34. finish();
  35. }
  36. }

我们将Handler声明为静态内部类,这样就不会持有外部类的引用,Runnable也一并如此处理。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注