プログラマってこんなかんじ??

アプリ作ったり歌ったりしてます

別スレッドでキュー管理(Handler, Looper, HandlerThread)

非同期処理を組んでると、
「別スレッド上でも Handler みたいなキュー管理がしたい」
って時があったりしますよねー。(きっと)


そんなとき、HandlerThread を使うとわりと簡単に実装できたりする。


HandlerThread(公式)


HandlerThread は java.lang.Thread を継承したクラスで、
android標準の Handler と組み合わせて使うための仕組みが入ってたり。


具体的な使い方。


まずは Handler(defaultコンストラクタ) を使う場合。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Handler handler = new Handler();
    handler.post(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    });
}

実行すると当然 「main」 と出力される。
当然、mainスレ上でキューが実行される。
これは問題ないですね。


次に Handler+HandlerThread の場合。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // 別スレ生成 -> 開始
    HandlerThread handlerThread = new HandlerThread("other");
    handlerThread.start();

    Handler handler = new Handler(handlerThread.getLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    });
}

実行すると 「other」 と出力される。
キューが実行されてるスレッドが mainスレ から 別スレ に変更されます。
もちろんこの Handlerインスタンス は別スレ上で使用してOK。


手順をまとめるとこんなかんじ。

  1. 別スレッド(HandlerThread)を生成
  2. 別スレ開始
  3. HandlerThreadインスタンス から Looperインスタンス 取得
  4. Handlerインスタンスを生成(3で取得した Looperインスタンス を 引数指定)
  5. 4 で作成した Handlerインスタンス を使用(Handler#post 等)


今まで PriorityBlockingQueue とか使って、
自前のキュー管理クラス作ってた自分涙目。
キューをFIFOで実行するだけのキュー管理であれば、
このやり方でまったく問題なさそうですね。


framework ではどうやってるのかなーと思い、
Looper、Handler、HandlerThread のソースを読んでみた。


ポイントはこのあたり。

  • コンストラクタ Handler(default) は「mainスレッド上で作成された」 Looperインスタンス を使用
  • コンストラクタ Handler(Looper) は「引数で渡された」 Looperインスタンス を使用
  • Looperインスタンスは Looper#prepare を呼び出すと生成される(ThreadLocal保持)
  • HandlerThread は Thread を継承したクラス
  • HandlerThread#run の中で Looper#prepare を呼び出している


Looperインスタンスの生成場所 の違い。
なるほど。