zoukankan      html  css  js  c++  java
  • 关于handler内存泄露的问题

    在使用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 的project师 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。

  • 相关阅读:
    洛谷P1762 偶数
    复习1
    zoj3329 One Person Game
    poj2096 Collecting Bugs
    hdu4035 Maze
    Cogs 2856. [洛谷U14475]部落冲突
    洛谷P2474 [SCOI2008]天平
    洛谷P3802 小魔女帕琪
    清北刷题冲刺 11-03 p.m
    清北刷题冲刺 11-03 a.m
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7337753.html
Copyright © 2011-2022 走看看