365連休

にわかのandroidとかの開発メモ。

Android Studio 3.6 ワーカースレッドとしてHandlerThreadを使う

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 AtomicReference mainHandlerRef = 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を指す
        }
    }
}