Android - Handlerの使用方法、および例

By JS | Last updated: December 29, 2019

Handlerは、Androidの基本的なイベントを処理するシステムです。 Handlerを使用する理由は、非同期(Asynchronous)にどのようなJobを処理するためです。

ActivityのMain threadはUI threadと呼ばれ、UIを処理するときに時間がかかる作業を実行すると、画面がスラッシングまたはANR(Android Not Responding)のような問題が発生します。

この記事では、次のようなものを認識する予定です。

  1. Handlerの使用方法
  2. Looperを用いて、他のThreadでHandlerを使用する方法
  3. Threadクラスを利用して、他のThreadでHandlerを使用する方法

この記事は、Kotlinで作成されました。この記事で使用した例では、GitHubで確認することができます。

Handle基本使用方法

まず、次のようにHandlerクラスを実装することができます。 最も重要なことは、 handleMessage()を実装する必要がすることです。 イベントがHandlerに渡される handleMessage()で処理されるからです。

class MyHandler : Handler() {
    companion object {
        const val TAG = "MyHandler"
        const val MSG_DO_SOMETHING1 = 1
        const val MSG_DO_SOMETHING2 = 2
        const val MSG_DO_SOMETHING3 = 3
        const val MSG_DO_SOMETHING4 = 4
    }
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            MSG_DO_SOMETHING1 -> {
                Log.d(TAG, "Do something1")
            }
            MSG_DO_SOMETHING2 -> {
                Log.d(TAG, "Do something2")
            }
            MSG_DO_SOMETHING3 -> {
                Log.d(TAG, "Do something3")
            }
            MSG_DO_SOMETHING4 -> {
                Log.d(TAG, "Do something4, arg1: ${msg.arg1}," +
                      " arg2: ${msg.arg2}, obj: ${msg.obj}")
            }
        }
    }
}

次のようにHandlerを生成し、イベントを渡すことができます。

// 1
val handler: Handler = MyHandler()

// 2
val msg: Message = handler.obtainMessage(MyHandler.MSG_DO_SOMETHING1)
handler.handleMessage(msg)

// 3
handler.sendEmptyMessage(MyHandler.MSG_DO_SOMETHING2)

// 4
val msg2 = Message.obtain(handler, MyHandler.MSG_DO_SOMETHING3)
handler.handleMessage(msg2)
  1. Handlerを生成するコードです。
  2. Messageオブジェクトを作成し、 handleMessage()でイベントを送出します。
  3. sendEmptyMessage()のイベントを引数として入れて渡すこともできます。
  4. obtain()メソッドを利用して、Messageオブジェクトを作成し、イベントを渡すことができます。

実行すると、次のように出力がされます。

12-29 19:36:08.469  7438  7438 D MyHandler: Do something1
12-29 19:36:08.469  7438  7438 D MyHandler: Do something2
12-29 19:36:08.469  7438  7438 D MyHandler: Do something3

上記のログから '7438 7438'は順番にProcessID ThreadIDを意味します。 PIDとTIDが同じではMain threadであり、UI threadと呼ばれます。

イベント処理の遅延

イベントを送信する、すぐに処理せずに一定時間後に処理することができるようにすることができます。

// 1
handler.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING1, 5000)
// 2
handler.sendEmptyMessage(MyHandler.MSG_DO_SOMETHING2)
// 3
handler.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING3, 2000)
  1. MSG_DO_SOMETHING1イベントを5000msの後に処理するようにします。
  2. MSG_DO_SOMETHING2を遅延なく処理します。
  3. MSG_DO_SOMETHING3を2000msの後に処理するようにします。

結果を見れば、上に設定した間隔で実行された。

12-29 18:09:35.743  5149  5149 D MyHandler: Do something2
12-29 18:09:37.743  5149  5149 D MyHandler: Do something3
12-29 18:09:40.744  5149  5149 D MyHandler: Do something1

データと一緒にイベントを転送する

上記の例では、イベントのみを転送することでした。 イベントを転送するときに、引数も渡すことができます。

val msg = handler.obtainMessage(MyHandler.MSG_DO_SOMETHING4)
msg.arg1 = 10              // 1
msg.arg2 = 100             // 1
msg.obj = Person("chacha") // 2
handler.sendMessage(msg)
  1. arg1、arg2は int変数です。 integerを渡すときに使用することができます。
  2. objは Object変数であり、オブジェクトの1つを送信することができます。

上記のコードの実行結果です。

12-29 18:18:10.666  5406  5406 D MyHandler: Do something4, arg1: 10, arg2: 100, obj: Person(name=chacha)

他のThreadでHandlerイベント処理(Looper)

もし時間がかかる作業がある場合はUI threadで処理するといけません。 このような場合、他のThreadで処理されるように実装する必要があります。

事実HandlerはLooperという名前のオブジェクトを使用して動作します。 1つのThreadに1つのLooperがあると考えてください。 したがって、Handlerが、他のスレッドで動作して作成するには、他のスレッドのLooperでHandlerを動作させる必要があります。

Handlerに引数としてLooperを渡さない場合、実行中のThreadのLooperを使用します。 次のようにLooperを引数として受け取るHandlerを作成しました。

class MyHandlerWithLooper(looper: Looper) : Handler(looper) {
    companion object {
        const val TAG = "MyHandler"
        const val MSG_DO_SOMETHING1 = 1
        const val MSG_DO_SOMETHING2 = 2
        const val MSG_DO_SOMETHING3 = 3
        const val MSG_DO_SOMETHING4 = 4
    }
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            MSG_DO_SOMETHING1 -> {
                Log.d(TAG, "Do something1")
            }
            MSG_DO_SOMETHING2 -> {
                Log.d(TAG, "Do something2")
            }
            MSG_DO_SOMETHING3 -> {
                Log.d(TAG, "Do something3")
            }
            MSG_DO_SOMETHING4 -> {
                Log.d(TAG, "Do something4, arg1: ${msg.arg1}," +
                      " arg2: ${msg.arg2}, obj: ${msg.obj}")
            }
        }
    }
}

上記のクラスは、次のように生成することができます。

// 1
val handlerThread = HandlerThread("MyHandlerThread")
// 2
handlerThread.start()

// 3
val handlerWithLooper = MyHandlerWithLooper(handlerThread.looper)

// 4
Log.d(TAG, "This is UI Thread!")
handlerWithLooper.sendEmptyMessage(MyHandler.MSG_DO_SOMETHING1)
handlerWithLooper.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING2, 1000)
handlerWithLooper.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING3, 2000)
  1. HandlerThreadはHandlerに使用する目的で作成しThreadです。
  2. start()を呼び出す必要がありThreadが動作します。
  3. Handlerに引数としてLooperを渡して生成します。
  4. handlerにイベントを転送します。

実行結果です。 TID(Thread ID)を見ればUI threadではないことを知ることができます。

12-29 18:26:16.613  5800  5800 D MainActivity: This is UI Thread!
12-29 18:26:16.613  5800  5829 D MyHandler: Do something1
12-29 18:26:17.613  5800  5829 D MyHandler: Do something2
12-29 18:26:18.614  5800  5829 D MyHandler: Do something3

他のThreadでHandlerイベント処理(Thread)

Threadクラスを実装し、そのThreadのLooperでHandlerを作成することができます。

次のコードは、クラス定義とクラスを作成して、イベントを送出するためのコードです。

class MyThreadForHandler : Thread() {
    private var myHandler: MyHandler? = null
    override fun run() {
        Looper.prepare()          // 1
        myHandler = MyHandler()   // 2
        Looper.loop()             // 3
    }

    fun doSomething() {
        myHandler!!.sendEmptyMessage(MyHandler.MSG_DO_SOMETHING1)
        myHandler!!.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING2, 1000)
        myHandler!!.sendEmptyMessageDelayed(MyHandler.MSG_DO_SOMETHING3, 2000)
    }
}


// 4
val myThreadForHandler = MyThreadForHandler()
myThreadForHandler.start()

// 5
Log.d(TAG, "This is UI Thread!")
myThreadForHandler.doSomething()
  1. Looper.prepare()はLooperを生成し、Looperがイベントを処理することができる状態にします。
  2. Looper初期化の後にHandlerを作成すると、実行中のThreadのLooperはHandlerに設定されます。
  3. Looper.loop()はLooperを実行させるAPIです。今イベントが来たらLooperが処理されます。

4.実装したThreadオブジェクトを作成します。 start()を呼び出すと、クラスの run()メソッドがcallbackされます。 5.クラスの関数を呼び出して、Handlerでイベントを伝達するようにします。

実行結果です。 TIDを見ればHandlerはUI threadと他のThreadで動作しました。

12-29 18:38:42.997  6973  6973 D MainActivity: This is UI Thread!
12-29 18:38:42.998  6973  6999 D MyHandler: Do something1
12-29 18:38:43.999  6973  6999 D MyHandler: Do something2
12-29 18:38:44.998  6973  6999 D MyHandler: Do something3

この記事で使用した例では、GitHubで確認することができます。

参考

Related Posts

codechachaCopyright ©2019 codechacha