Coroutineと非同期処理のComposeでの活用【Jetpack Compose入門⑭】

Coroutineとは?

Coroutineの概要

Coroutine(コルーチン)は、Kotlinで非同期処理を簡単に記述するための仕組みです。従来のスレッド管理とは異なり、軽量で効率的に非同期処理を実行できます。

Coroutineの主な特徴

  • 軽量スレッド: 少ないリソースで複数の非同期タスクを実行可能
  • 簡潔なコード: コールバックを使わず、直感的に非同期処理を記述
  • スコープ管理: CoroutineScope を使用してタスクのライフサイクルを適切に管理
  • 非同期処理の統合: suspend 関数を使用して、処理の中断と再開をシンプルに実装可能

Coroutineの基本構造

Coroutineの起動

Coroutineは launchasync を使用して起動できます。

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("Coroutine実行完了")
    }
    println("メインスレッド処理")
}
  • launch は新しいコルーチンを開始
  • delay は一時停止処理(スレッドブロックなし)
  • runBlocking はメインスレッドでのブロッキング動作

async と await を使った並列処理

async を使用すると、複数の非同期処理を並列で実行し、結果を取得できます。

suspend fun fetchData(): String {
    delay(2000L)
    return "データ取得完了"
}

fun main() = runBlocking {
    val result = async { fetchData() }
    println("データ取得中...")
    println(result.await())
}

Jetpack ComposeでのCoroutine活用

Composeにおける非同期処理

Jetpack Composeでは、LaunchedEffectrememberCoroutineScope を使用して非同期処理を組み込むことが可能です。

LaunchedEffect の使用

LaunchedEffect を利用すると、特定の状態が変化した際に非同期処理を実行できます。

@Composable
fun FetchDataScreen() {
    var data by remember { mutableStateOf("読み込み中...") }

    LaunchedEffect(Unit) {
        data = fetchData()
    }

    Text(text = data)
}
  • LaunchedEffect(Unit) はコンポーネントの初回描画時に実行
  • fetchData()suspend 関数として非同期実行

rememberCoroutineScope を使用したボタン処理

ボタンを押したときに非同期処理を開始する場合、rememberCoroutineScope を活用できます。

@Composable
fun AsyncButtonScreen() {
    val coroutineScope = rememberCoroutineScope()
    var message by remember { mutableStateOf("ボタンを押してください") }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Button(onClick = {
            coroutineScope.launch {
                message = fetchData()
            }
        }) {
            Text("データ取得")
        }
        Text(text = message)
    }
}
  • rememberCoroutineScope を使うことで、ボタン押下時に非同期処理を実行
  • launch を使用して、非同期処理を開始

ViewModelとCoroutineの連携

ViewModelの作成

Jetpack Composeでは、ViewModelを使用してデータの状態を管理することが推奨されます。

class DataViewModel : ViewModel() {
    private val _data = MutableStateFlow("データ未取得")
    val data: StateFlow<String> = _data.asStateFlow()

    fun fetchData() {
        viewModelScope.launch {
            _data.value = fetchData()
        }
    }
}

ViewModelをComposeに適用

collectAsState() を使用して、ViewModelのデータをCompose UIで監視します。

@Composable
fun ViewModelScreen(viewModel: DataViewModel = viewModel()) {
    val data by viewModel.data.collectAsState()

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Button(onClick = { viewModel.fetchData() }) {
            Text("データ取得")
        }
        Text(text = data)
    }
}
  • collectAsState() を使用し、ComposeのUIが自動的に更新されるようにする
  • viewModelScope.launch で非同期処理を実行し、データを更新

非同期処理のエラーハンドリング

try-catch を使用したエラー処理

Coroutine内でエラーが発生した場合は、try-catch を使用して適切に処理します。

suspend fun fetchDataSafe(): String {
    return try {
        delay(2000L)
        "データ取得成功"
    } catch (e: Exception) {
        "エラー発生: ${e.message}"
    }
}

CoroutineExceptionHandler の使用

CoroutineExceptionHandler を使用すると、グローバルなエラーハンドリングが可能です。

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    println("エラー: ${exception.message}")
}

fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob() + exceptionHandler)
    scope.launch {
        throw RuntimeException("テストエラー")
    }
}

まとめ

  • Coroutineは軽量で効率的な非同期処理を提供
  • LaunchedEffectrememberCoroutineScope を使用してComposeに適用可能
  • ViewModelと組み合わせることで、データ管理をより適切に実装
  • エラーハンドリングを適切に行い、安全な非同期処理を実装する

Jetpack ComposeとCoroutineを組み合わせることで、パフォーマンスの高いモダンなアプリ開発を実現できます。実際にコードを書いて試してみましょう!