Handler の派生クラスを非 static class として Activity などに実装すると、次のような警告がでます。
NG例
public class MyActivity extends Activity {
private class MyHandler extends Handler {
}
}
public class MyActivity extends Activity {
private Handler h = new Handler() {
}
}
警告
This Handler class should be static or leaks might occur
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected.
If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue.
If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
一つづつ解釈していくと、
このHandlerクラスは静的である必要があります または リークが発生する可能性があります
(もはやこの時点で、「設計ミスだろ」とつっこみたいところではある・・)
このハンドラは内部クラスとして宣言されているため、外部クラスがガベージコレクションされない可能性があります。
ハンドラがメインスレッド以外のスレッドのためにLooperまたはMessageQueueを使用している場合、問題はありません。
Worker thread なら問題ないらしい。
ハンドラがメインスレッドのLooperまたはMessageQueueを使用している場合は、次のようにハンドラ宣言を修正する必要があります。
ハンドラを静的クラスとして宣言します。
外部クラスでは、外部クラスへのWeakReferenceをインスタンス化し、Handlerをインスタンス化するときにこのオブジェクトをHandlerに渡します。
WeakReferenceオブジェクトを使用して、外部クラスのメンバーへのすべての参照を作成します。
結局 Handler 自体は leak してるってことでしょ!
static じゃなければ外部クラス (Activity とか) の instance を保持してしまうのは当たり前で、要は Activity とか leak すると重くなるから気をつけてね。弱参照はアプリでやってね。みたいな・・
なぜ leak するのか?
こちらにわかりやすくまとめられていますが、
https://www.notice.co.jp/archives/3660
- Handler は Message を Looper へキューイング
- LooperはキューからMessageを取り出して、HandlerのdispatchHandlerにMessageを渡して、handleMessageで処理させます。
- HandlerはMessageの中にタスク(Runnableを実装したもの)があれば、そのタスクを実行します。
Main Looper は唯一の存在 (つまり static) で、
その Main Looper は実行する Message を保持していて、
Message は post 先の Handler の instance を保持している。
たとえば、 sendMessageDelayed とかで長時間 Message を保持していた場合、その時間だけ Handler も外部クラスも leak することになる。
という理解で概ね間違ってないと思います。
(やっぱり設計ミスだよね・・?)
対策?
上記の理屈でいくと、以下のいずれかの対策をすることで問題を回避できると思われる。
- 長時間 message を保持するような実装をしない
- Handler を static にして、外部クラスを WeakReference で保持する
- WeakReferenceHandler 的なのを作る
WeakReferenceHandler?
こんな感じ・・?
public class WeakReferenceHandler {
private final WeakReference<Handler.Callback> mWeakReference;
public WeakReferenceHandler(Handler.Callback callback) {
mWeakReference = new WeakReference<>(callback);
}
@Override
public void handleMessage(Message msg) {
final Handler.Callback callback = mWeakReference.get();
if (callback != null) {
callback.handleMessage(msg);
}
}
}
こんなんでいいかな・・?
動作確認もしてないのでよくわかりませんが。。
各アプリがこんなことしなきゃいけないのは本当にばかばかしいなぁ。。