zoukankan      html  css  js  c++  java
  • Handler的内存泄露分析

      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

  • 相关阅读:
    redis 命令行 操作
    redis php sort 函数
    redis php 实例二
    redis php 实例一
    redis 分布式,主从同步
    inux redis 安装配置, 以及redis php扩展
    linux memcache 安装
    推荐linux命令在线查,简约而不简单
    基于Bootstrap样式的 jQuery UI 控件 (v0.5).
    C语言中文网
  • 原文地址:https://www.cnblogs.com/fishbone-lsy/p/5303399.html
Copyright © 2011-2022 走看看