Android handlerのContextメモリ漏洩
まず次のコードを見てください.
ここで簡単にhandlerを宣言します.すべて正常に見えますが、この書き方はメモリの漏洩を引き起こす可能性があります.理由はコンパイラが教えてくれたからです.この書き方には警告があります.
AndroidでHandlerはstaticと宣言されるべきです.そうしないとメモリの漏洩の問題が発生する可能性があります.
なぜメモリが漏れる可能性があるのでしょうか?既存の情報を分析します Androidアプリケーションが最初に起動すると、システムはアプリケーションのメインスレッドにLoopオブジェクトを作成し、Loopはメッセージキュー(Message queue)であり、次々と情報を処理する.アプリケーションの主なイベント、たとえばActivityのライフサイクル、クリックイベントなどは、メッセージオブジェクトに含まれます.メッセージはLoopのメッセージキューに並んで処理されるのを待つので、このプライマリスレッドのLoopはアプリケーションのライフサイクル全体にわたって常に 存在する.現在、私たちのhandlerはメインスレッドで宣言されており、自然にメインスレッドLoopのメッセージキューに関連付けられています.このとき、メッセージキューに伝達するメッセージにはhandlerの参照がある.なぜなら、メッセージが処理されると、システムはHandlerの を自分で呼び出す必要があるからである. javaでは静的でない内部匿名クラス(つまりHandleを宣言する方法)は、静的な内部クラスではできないが、彼の外部クラスの暗黙的なアプリケーションを持っている.
以上の点に基づいてこの例を修正する
宣言されたhandlerは10分遅延して情報を送信し、finish()は現在のActivityを削除し、上記の2点目に従って10分遅延して送信された情報は、メインスレッドのメッセージキューに残り、10分待ってから送信されます.問題は、メッセージがhandlerの参照を持っているため、handlerは匿名の内部クラスであり、匿名の内部クラスは彼の外部クラス(つまりLeakActivity)の暗黙的な参照を持っているが、LeakActivityはfinish()によって削除されているため、Contextのメモリ漏洩を引き起こす可能性がある.上のrunnableも匿名の内部クラスであることに注意してください.
この隠れた危険をどのように解決するかは、静的な内部クラスを使用することを知っていると信じています.これにより、外部の暗黙的な参照は保持されません.
handlerとrunnableの両方を静的内部クラスとして宣言し、handlerが所有するContextをWeakReferenceとして宣言します.
FBI Warning
内部クラスがactivityのライフサイクル以外に存在する場合は、外部クラス(activity)の暗黙的な参照を持っているため、非静的な内部クラスは使用しないでください.それを静的に宣言する
public class LeakActivity extends Activity {
public Handler leakHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
}
};
ここで簡単にhandlerを宣言します.すべて正常に見えますが、この書き方はメモリの漏洩を引き起こす可能性があります.理由はコンパイラが教えてくれたからです.この書き方には警告があります.
AndroidでHandlerはstaticと宣言されるべきです.そうしないとメモリの漏洩の問題が発生する可能性があります.
なぜメモリが漏れる可能性があるのでしょうか?既存の情報を分析します
handleMessage(Message msg)
メソッド以上の点に基づいてこの例を修正する
public class LeakActivity extends Activity {
public Handler leakHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
};
protected void onCreate(android.os.Bundle savedInstanceState) {
setContentView(R.layout.activity_leak);
//10
leakHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}, 1000 * 60 * 10);
// Activity
finish();
};
宣言されたhandlerは10分遅延して情報を送信し、finish()は現在のActivityを削除し、上記の2点目に従って10分遅延して送信された情報は、メインスレッドのメッセージキューに残り、10分待ってから送信されます.問題は、メッセージがhandlerの参照を持っているため、handlerは匿名の内部クラスであり、匿名の内部クラスは彼の外部クラス(つまりLeakActivity)の暗黙的な参照を持っているが、LeakActivityはfinish()によって削除されているため、Contextのメモリ漏洩を引き起こす可能性がある.上のrunnableも匿名の内部クラスであることに注意してください.
この隠れた危険をどのように解決するかは、静的な内部クラスを使用することを知っていると信じています.これにより、外部の暗黙的な参照は保持されません.
public class LeakActivity 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);
/** * Runnable * */
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//10
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Activity
finish();
}
}
handlerとrunnableの両方を静的内部クラスとして宣言し、handlerが所有するContextをWeakReferenceとして宣言します.
FBI Warning
内部クラスがactivityのライフサイクル以外に存在する場合は、外部クラス(activity)の暗黙的な参照を持っているため、非静的な内部クラスは使用しないでください.それを静的に宣言する