1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

This Handler class should be static or leaks might occur

Last updated at Posted at 2017-11-07

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

  1. Handler は Message を Looper へキューイング
  1. LooperはキューからMessageを取り出して、HandlerのdispatchHandlerにMessageを渡して、handleMessageで処理させます。
  2. 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);
        }
    }
}

こんなんでいいかな・・?
動作確認もしてないのでよくわかりませんが。。
各アプリがこんなことしなきゃいけないのは本当にばかばかしいなぁ。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?