【Kotlin/Android Studio】Handlerの使い方!遅延や周期で処理を繰り返す方法

この記事からわかること

  • Android Studio/KotlinHandler使い方
  • MessageQueueRunnableLooper役割違い
  • カウントアップタイマー実装方法
  • 遅延処理:postDelayedメソッド
  • スレッド操作

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

公式リファレンス:Handler

環境

Handlerとは?

AndroidのHandlerとはスレッドと非同期で実行される処理に関連するタスクを管理するためのクラスです。Handlerを使用することでタイマーの設定や非同期的なタスクの処理、UIに関する作業をサブスレッドで行いたい場合などさまざまな機能を実現することができます。

Handlerを理解するためにはMessageQueueRunnableオブジェクト、Looperの存在が重要になってきます。

MessageQueueとは?

公式リファレンス:MessageQueue

MessageQueueはHandlerLooperなどで使用される機能でアプリケーション内のスレッド間の通信を管理する役割を持っています。MessageQueueはスレッド1つに対して1つ関連づけられており、そのスレッドで処理するタスクを順番に処理していくための構造を持っています。Looper.myQueue()現在のスレッドのMessageQueueを取得することができます。

// 現在のスレッドのMessageQueueを取得する
Looper.myQueue()

HandlerMessageQueueにタスクを追加し、Looperタスクを取り出し処理を実行していきます。管理されるタスクはメッセージと呼ばれる通信を目的としたタスクとRunnableオブジェクトのような具体的な処理を目的としたタスクの2種類が管理されています。

MessageQueueとは?〜まとめ〜

Runnableオブジェクトとは?

公式リファレンス:Runnableオブジェクト

public interface Runnable

RunnableオブジェクトとはMessageQueueに格納されるタスクの種類の1つです。このオブジェクトが実行すべきコード(処理)を保持しています。保持している場所はrunメソッド中であり、このメソッドは引数なしかつ返り値のない(Void)のメソッドで定義されています。

public abstract void run ()

Runnableオブジェクト?〜まとめ〜

Looperとは?

公式リファレンス:Looper

Looperは自身が管理するMessageQueue内のタスクを順番に処理していく役割を持っています。スレッド1つにLooper1つが関連づいているため、マルチスレッド処理を行うためには欠かせない仕組みになります。

例えばUI(メイン)スレッドのLooperを取得したい場合はgetMainLooperメソッドを使用します。

// UI(メイン)スレッドのLooperを取得
val uiThreadLooper = Looper.getMainLooper()

Looperとは?〜まとめ〜

Handlerのインスタンス化

Handlerインスタンス化する時は引数に対象のLooperを指定して両者を関連づけます。Looperを渡さずにHandler()でインスタンス化することも可能ですが、公式から非推奨になっているようで、クラッシュを引き起こす原因になるようです。

// LooperとHandlerを関連付ける
uiThreadHandler = Handler(uiThreadLooper)

遅延処理:postDelayed

postDelayedメソッドを使用することで処理を任意のミリ秒数遅延させることができます。

val handler = Handler(Looper.getMainLooper())
handler.postDelayed({
    // 遅延実行する処理
}, 1000) // ミリ秒単位で遅延させる場合

カウントアップタイマーを実装してみる

最低限の使い方がわかった上でHandlerLooperを使用してカウントアップタイマーを実装してみたいと思います。とりあえず全体のコードは以下のような感じになりました。uiThreadHandler(Handlerオブジェクト)からいろいろなメソッドを呼び出してタスクを操作しているのがわかると思います。

class MainActivity : AppCompatActivity() {

    private var count = 0
    private lateinit var uiThreadHandler: Handler
    private lateinit var timerRunnable: Runnable
    private lateinit var resultLabel: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        resultLabel = findViewById(R.id.result_label)

        // UIスレッドのLooperを取得
        val uiThreadLooper = Looper.getMainLooper()

        // LooperとHandlerを関連付ける
        uiThreadHandler = Handler(uiThreadLooper)

        // タイマー用のRunnableを初期化
        timerRunnable = object : Runnable {
            override fun run() {
                // カウントをアップしてUIを更新
                count++
                uiThreadHandler.post {
                    updateUI("Count: $count")
                }
                // 1000ミリ秒(1秒)ごとに再度このRunnableを呼び出す
                uiThreadHandler.postDelayed(this, 1000)
            }
        }

        // タイマーを開始
        startTimer()
    }

    private fun updateUI(message: String) {
        // UIを更新する処理を書く
        resultLabel.text = message
    }

    private fun startTimer() {
        // 最初の呼び出しを0ミリ秒後に行うように設定
        uiThreadHandler.postDelayed(timerRunnable, 0)
    }

    override fun onDestroy() {
        super.onDestroy()
        // アクティビティが破棄される際にタイマーを停止
        uiThreadHandler.removeCallbacks(timerRunnable)
    }
}

キューにRunnableオブジェクトを送信

キューにRunnableオブジェクトを送信するにはpostメソッドを使用します。今回はキューの中にtimerRunnableオブジェクトが追加され、実行された中でUIを更新する処理をさらにキューへ追加する際に使用しています。

uiThreadHandler.post {
    updateUI("Count: $count")
}

遅延して実行させるタスクをキューへ送信

キューに追加したタスクを遅延実行させるためにはpostDelayedメソッドを使用します。1つ目の引数に対象のタスクを、2つ目の引数に遅延させたいミリ秒数を指定します。

// 1000ミリ秒(1秒)ごとに再度このRunnableを呼び出す
uiThreadHandler.postDelayed(this, 1000)

今回はRunnableオブジェクトの処理の最後で再度自身を1秒後に実行されるようにキューに送信することでループして処理を実行させています。

キュー内のRunnableをすべて削除

キュー内のRunnableをすべて削除させるためにはremoveCallbacksメソッドを使用します。タイマーが不要になるタイミングやアクティビティが破棄されるタイミングで明示的に削除しておきます。。

// メッセージキュー内のRunnableをすべて削除
uiThreadHandler.removeCallbacks(timerRunnable)

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index