handlerがactivityのメンバー変数によるメモリ漏洩

10663 ワード

handlerがactivityのメンバー変数によるメモリ漏洩


分類:android関連
2013-07-09 18:55 
622人が読む
コメント(0)
コレクション
通報する
まず簡単なコードのセットを見てみましょう
1
2
3
4
5
6
7
8
9
public class SampleActivity extends Activity {

  private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ... 
    }
  }
}
Activityにこのように書くと、Android Lintはwarning:In Android, Handler classes should be static or leaks might occur.を提示します.意味: Android ,Handler 。 
どうしてそうなの?Handlerについて
  • Androidプログラムが初めて作成されると、メインスレッドにLooperオブジェクトが同時に作成されます.Looperは、Messageオブジェクトの処理に続く簡単なメッセージキューを実現する.プログラムフレームワークのすべての主要なイベント(例えば、画面上のクリック時間、Activityライフサイクルの方法など)は、Messageオブジェクトに含まれ、Looperのメッセージキューに追加され、1つの処理が行われる.プライマリ・スレッドのLooperは、アプリケーション全体のライフサイクル内に存在します.
  • プライマリスレッドにHandlerオブジェクトが作成されると、Looperのmessage queueに関連付けられます.Messageがメッセージキューに追加するとMessageは現在のHandler参照を保持し、Looperが現在のメッセージに処理するとHandler#handleMessage(Message)が呼び出される.
  • javaでは、no-staticの内部クラスは、現在のクラスの参照を暗黙的に保持します.staticのクラスはありません.

  • メモリの漏洩はどこで起きたのでしょうか.次のコードを見てみましょう
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class SampleActivity extends Activity {
    
      private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // ...
        }
      }
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        //  10 
        mHandler.postDelayed(new Runnable() {
          @Override
          public void run() { }
        }, 600000);
    
        //  Activity
        finish();
      }
    }
    
    Activityが終了すると、Message queueがこのMessageを処理する前に、それは生存し続ける.このMessageHandlerの参照を持っているが、HandlerActivity(SampleActivity)の参照を持っている.このActivityのすべてのリソースは、このメッセージ処理の前に回収されず、メモリ漏洩が発生している.
    解決策は、次のコードを見てください.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class SampleActivity extends Activity {
    
      /**
       *  , 
       */
      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);
    
      /**
       *  , 
       */
      private static final Runnable sRunnable = new Runnable() {
          @Override
          public void run() { }
      };
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        //   10 
        mHandler.postDelayed(sRunnable, 600000);
    
        //  
        finish();
      }
    }
    

    OK、終了
    NOTE:弱い引用のActivityが回収されるのではないかと心配する人が多いですが、これは全く心配しないでください.私たちがこのインタフェースにいるとき、このActivityは回収されませんから.もし私たちのこのActivityが回収されたら、私たちのこのインタフェースはどのように存在しますか.
    NOTE 2:皆さん、私はAsyncTaskを参照して、ドキュメントと自分の理解を見て、私が説明できないところがあります.
    NOTE 3:具体的にどのように漏洩を防止するか私もはっきり言えませんが、Handlerの下にソースコードがあります.
    1
    2
    3
    4
    5
    6
     final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }