どうも。どっことです。今回はKotlinCoroutineのテーマです。
Kotlin Coroutine
Kotlin Coroutine
は非同期処理などのコールバック地獄を解消してくれたり、可読性の高いコードを実装できるようになる便利な機能ですが、「実際のところ、どう使うのが良いの?」と考えました。今回はそんな使うと便利だけど実際どう使うかイマイチ分からない人向けに、ベストプラクティスのページの内容を簡単に紹介したいと思います。
TL;DR(結論)
ViewModel
を実装してviewModelScope.launch
/View側ではlifecycleScope.launch
を使うのがよさそうです。
Google開発者サイトでコルーチンに関するベストプラクティスが紹介されています。コルーチンにフォーカスしたものだと、以下が挙げられています。
Dispatcher
を外から注入できるようにするViewModel
でコルーチンを作成するGlobalScope
は使わない- コルーチンをキャンセルできるようにする
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のベストプラクティスの記事からさまざまなページにアクセスできるので、ぜひ参考にしてください。