Androidの非同期処理にAsyncTaskやExecutorを使う事があるが、ワーカースレッドを作りworkerHandler.post()で実行する方法もある。
最近のマイブーム。
HandlerThreadはThreadを継承しているため、newしてstart()すれば、別スレッドでお手軽非同期処理ができる。
public class MainActivity extends AppCompatActivity { /** ワーカースレッド */ private final @NonNull HandlerThread workerHandlerThread = new HandlerThread("XXX#workerHandlerThread"); //contextをどうにかしてcomponent nameがいいのかも /** ワーカースレッドのハンドラ */ private final @NonNull Handler workerHandler; { //直ちに開始する workerHandlerThread.start(); //postするためのハンドラを取得 //getLooper()内で、Looperが回りだすまで呼び出しスレッドがロックするので注意 workerHandler = new Handler(workerHandlerThread.getLooper()); //post例 workerHandler.post(new Runnable() { @Override public void run() { //do something } }); } }
画面更新などはメインスレッドで行わないといけないため、ユーティリティ関数もどうぞ。
// 2023/4/11修正 Handlerを強参照で保持。スレッドセーフ化。 private static final @NonNull AtomicReferencemainHandlerRef = new AtomicReference<>(); public static void postMainThread(@NonNull Runnable runnable){ synchronized(mainHandler){ @Nullable Handler handler = mainHandlerRef.get(); if(handler==null) //メインスレッドのLooperをHandlerへ設定 mainHandlerRef.set(handler = new Handler(Looper.getMainLooper()) ); //post handler.post(runnable); } }
バグなどで、誤ってHandlerThreadのrun()を直接呼ぶと、よくわからないスタックトレースと共に
LooperどうにかなっちゃったException
が発生するので注意。
workerHandler.post(workerHandlerThread); //oops!
HandlerThreadは継承して使うこともできる。
public class MyHandlerThread extends HandlerThread { private Handler handler; /** Looperが完成したら呼ばれる */ @Override protected void onLooperPrepared() { super.onLooperPrepared(); handler = new Handler(getLooper()); //必要があれば、post handler.post(func1); } private final @NonNull Runnable func1 = new Runnable() { @Override public void run() { //do something //handler.post(this); //リポスト //ここのthisはRunnableを指す } } }