Handlerは、Androidの基本的なイベントを処理するシステムです。 Handlerを使用する理由は、非同期(Asynchronous)にどのようなJobを処理するためです。
ActivityのMain threadはUI threadと呼ばれ、UIを処理するときに時間がかかる作業を実行すると、画面がスラッシングまたはANR(Android Not Responding)のような問題が発生します。
この記事では、次のようなものを認識する予定です。
- Handlerの使用方法
- Looperを用いて、他のThreadでHandlerを使用する方法
- 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)
- Handlerを生成するコードです。
- Messageオブジェクトを作成し、
handleMessage()
でイベントを送出します。 sendEmptyMessage()
のイベントを引数として入れて渡すこともできます。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)
MSG_DO_SOMETHING1
イベントを5000msの後に処理するようにします。MSG_DO_SOMETHING2
を遅延なく処理します。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)
- arg1、arg2は
int
変数です。 integerを渡すときに使用することができます。 - 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)
- HandlerThreadはHandlerに使用する目的で作成しThreadです。
start()
を呼び出す必要がありThreadが動作します。- Handlerに引数としてLooperを渡して生成します。
- 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()
Looper.prepare()
はLooperを生成し、Looperがイベントを処理することができる状態にします。- Looper初期化の後にHandlerを作成すると、実行中のThreadのLooperはHandlerに設定されます。
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
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法