ラベル KotlinCoroutine の投稿を表示しています。 すべての投稿を表示
ラベル KotlinCoroutine の投稿を表示しています。 すべての投稿を表示

2025年4月19日土曜日

Android そういえば Kotlin Coroutine ってどう使うのが良いの?

どうも。どっことです。今回はKotlinCoroutineのテーマです。

Kotlin Coroutine

Kotlin Coroutineは非同期処理などのコールバック地獄を解消してくれたり、可読性の高いコードを実装できるようになる便利な機能ですが、「実際のところ、どう使うのが良いの?」と考えました。今回はそんな使うと便利だけど実際どう使うかイマイチ分からない人向けに、ベストプラクティスのページの内容を簡単に紹介したいと思います。

TL;DR(結論)

ViewModelを実装してviewModelScope.launch/View側ではlifecycleScope.launchを使うのがよさそうです。

Google開発者サイトでコルーチンに関するベストプラクティスが紹介されています。コルーチンにフォーカスしたものだと、以下が挙げられています。

  1. Dispatcherを外から注入できるようにする
  2. ViewModelでコルーチンを作成する
  3. GlobalScopeは使わない
  4. コルーチンをキャンセルできるようにする

Dispatcherを外から注入できるようにする

withContextなどで引数に設定するDispatcher(どのスレッドで処理するかの指定)は、ハードコードせず外から注入できる形にした方が良いです。テストコードを実装するときにテストが容易にできるようになります。

// OK:処理するDispatcherを外から指定する
class SampleRepository(private val ioDispatcher: CoroutineDispatcher =  Dispatchers.IO) {
    suspend fun veryHeavyTask() = withContext(ioDispatcher) {
        // very heavy task
    }
}
class SampleRepository() {
    // NG: Dispatcherをハードコード(直接指定)しない
    suspend fun veryHeavyTask() = withContext(Dispatchers.IO) {
        // very heavy task
    }
}

ViewModelでコルーチンを作成する

ViewModelを作成し、そこでコルーチンを作成→非同期処理を実装するのがよいとのことです。

そして画面側に非同期処理が必要なことを意識させることがないよう、suspend関数として公開しないよう実装するのが良いとのことです。

class SampleViewModel(): ViewModel() {
    private val repository = SomeRepository()

    // NG:UI側にsuspend関数(=非同期処理)として公開しない
    suspend fun badSample() = repository.veryHeavyTask()

    // OK:ViewModel内でコルーチンを生成し非同期処理を実行する
    fun sample() {
        viewModelScope.launch {
            repository.veryHeavyTask()
        }
    }
}

GlobalScopeを使わない

テストコードの実装しにくさ、GlobalScopeをハードコードしていることによりDispatcherもハードコードされやすくなる点などから、GlobalScopeを利用しないことを推奨しています。

class SampleRepository() {
    fun veryHeavyTask() {
        // NG:GlobalScopeをハードコードしない
        GlobalScope.launch {
            // very heavy task
        }
    }
}

GlobalScopeの利用が正当であるケースはかなり稀で、アプリが生存期間中にアクティブな状態を維持しなければいけない状態とのことです。また、現在のスコープより長い生存期間が必要な処理であれば、CoroutineScopeを外部から指定するような構成とすることを検討すべきとのことです。

コルーチンをキャンセル可能にする

コルーチンはキャンセル可能ですが、実際コルーチン内でキャンセルされたか確認したり、停止したりするまではコルーチンはキャンセルされないとのことです。ensureActiveを使用することでキャンセルの確認ができます。

class SampleViewModel(): ViewModel() {
    private val repository = SampleRepository()

    fun sample(value: Int) {
        viewModelScope.launch { 
            val result = repository.validateValue(value)
            // キャンセルできるタイミングでアクティブか確認
            ensureActive()
            if (result) {
                repository.submit(value)
            }
        }
    }
}

まとめ

今回はKotlinCoroutineのベストプラクティスについて紹介しました。より詳しい内容は参考に記載したGoogleのベストプラクティスの記事からさまざまなページにアクセスできるので、ぜひ参考にしてください。

参考

2023年12月4日月曜日

Android Kotlin Coroutineを並行に実行する実装を解説

どうも。どっことです。今回はKotlin Coroutineの並行実行する処理の実装方法について解説していきます。

Kotlin Coroutineを並列に実行する

非同期処理をとっても素敵に実装できるKotlin Coroutineですが、いくつかの処理を同時に実行したいようなケースにも対応することができます。APIによる通信やデータベースデータの永続化といった、時間がかかる処理をできるだけ短縮していきましょう。

実装方法

実装だけなら以下の2パターンで実現することができます。

async/awaitによる実装

ひとつはasync/awaitを使って実装する方法です。

viewModelScope.launch {
    val apiList = listOf(
        // async でコルーチンを生成 その中でAPIを実行する
        async { api1() },
        async { api2() },
        async { api3() },
    )
    // すべてのコルーチンを実行。全部終わるまで待つ。
    apiList.awaitAll()
}

launch/joinAllによる実装

もうひとつはlaunch/joinAllを使って実装する方法です。

viewModelScope.launch {
    // lanuch でコルーチンを作成し、その中でAPIを実行する。
    val apiJobsList = listOf(
        launch { api1() },
        launch { api2() },
        launch { api3() },
    )
    // すべてのコルーチンを実行。全部終わるまで待つ。
    apiJobsList.joinAll()
}

まとめ

今回は、Kotlin Coroutineによる処理を並列実行する実装について解説しました。ユーザの待ち時間を減らせるように活用していただけると幸いです。

参考

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。