1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Android]Jetpack Compose から collectAsState 活用する

Posted at

スクリーンショット 2025-03-05 0.23.17.png

Android開発において、Jetpack ComposeはUIを構築するための最新のアプローチを提供します。特に、状態管理はComposeアプリケーションにおいて重要な要素であり、これを効果的に管理するために、ViewModelcollectAsState()を組み合わせて使用する方法について学びましょう。

📌 collectAsState()

collectAsState()は、Jetpack ComposeにおいてFlow(または StateFlowを簡単に購読し、その結果をComposeのStateに変換する拡張関数です。この関数を使用すると、Flowが新しい値をemitするたびにUIが自動的に再コンポジションされ、最新の状態が表示されます。

なぜ使用するのか?

  • Kotlin CoroutinesFlowは、非同期データをストリーミング方式で提供します。
  • しかし、ComposeではStateの変化を監視してUIを更新する必要があります。
  • collectAsState()を使用すると、Flowを直接launchしてcollectする必要がなく、自動的にFlowを購読し、現在の値をComposeの状態として扱うことができます。

📌 ViewModel と Flow

ViewModelでFlowを使用して状態を公開するパターン

class MainViewModel : ViewModel() {

    private val _uiState = MutableStateFlow(MyUiState())
    val uiState: StateFlow<MyUiState> = _uiState.asStateFlow()

    fun updateSomething(newValue: String) {
        _uiState.update { currentState ->
            currentState.copy(something = newValue)
        }
    }
}
  • MutableStateFlow : 内部で変更可能な状態を定義
  • StateFlow : 外部には読み取り専用で公開

collectAsState() の理解

@Composable
fun MainScreen(viewModel: MainViewModel = viewModel()) {

    val uiState by viewModel.uiState.collectAsState()

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column {
            Text(text = "Current value: ${uiState.something}")

            Button(onClick = { viewModel.updateSomething("New Value") }) {
                Text("Update Value")
            }

            if (uiState.isLoading) {
                CircularProgressIndicator()
            }

            uiState.error?.let { error ->
                Text(
                    text = error,
                    color = MaterialTheme.colorScheme.error
                )
            }
        }
    }
}
  • uiState : FlowをStateに変換
  • Compose が自動的にFlow購読のライフサイクルを管理
  • コンポーザブルがCompositionから削除されたとき、Flowの購読も自動的にキャンセル

これで uiState は State 型となり、
viewModel.uiState の値が変更されるたびにこのコンポーザブルが再構成されます。

📌 collectAsState を使わない場合の比較

@Composable
fun MainScreen(viewModel: MainViewModel = viewModel()) {

    var uiState by remember { mutableStateOf(MyUiState()) }
  
    LaunchedEffect(viewModel) {
        viewModel.uiState.collect { newState ->
            uiState = newState
        }
    }

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column {
            Text(text = "Current value: ${uiState.something}")

            Button(onClick = { viewModel.updateSomething("New Value") }) {
                Text("Update Value")
            }

            if (uiState.isLoading) {
                CircularProgressIndicator()
            }

            uiState.error?.let { error ->
                Text(
                    text = error,
                    color = MaterialTheme.colorScheme.error
                )
            }
        }
    }
}
  • LaunchedEffectを使用して手動でコルーチンのライフサイクルを管理する必要がある。
  • 実装を誤るとメモリリークの原因になる可能性がある。

📌 複数の Flow を処理する場合

collectAsState() を使用:

val state1 by flow1.collectAsState()
val state2 by flow2.collectAsState()
val state3 by flow3.collectAsState()

collectAsState() を使わない場合:

var state1 by remember { mutableStateOf(initialValue1) }
var state2 by remember { mutableStateOf(initialValue2) }
var state3 by remember { mutableStateOf(initialValue3) }

LaunchedEffect(Unit) {
    launch { flow1.collect { state1 = it } }
    launch { flow2.collect { state2 = it } }
    launch { flow3.collect { state3 = it } }
}
  • collectAsState() を使用すると、各 Flow を個別に管理でき、コードが明確になる。

📌 初期値の処理

collectAsState() を使用:

val state by flow.collectAsState(initial = initialValue)

collectAsState() を使わない場合:

var state by remember { mutableStateOf(initialValue) }
LaunchedEffect(Unit) {
    flow.collect { state = it }
}
  • collectAsState() は Flow の最初の値が収集される前に表示する初期値を直接指定可能。

📌 エラー処理

collectAsState() を使用:

  • エラー処理は内部で自動的に行われるため、特別な処理は不要。

collectAsState() を使わない場合:

LaunchedEffect(Unit) {
    try {
        flow.collect { state = it }
    } catch (e: Exception) {
        // error handling
    }
}
  • Flowの収集中に発生した例外を手動で処理する必要がある。

📌 まとめ

パフォーマンス比較

メモリ使用量: どちらの方法も類似したメモリ使用量を示しますが、collectAsState()は内部的に最適化されており、若干効率的な場合があります。

再コンポーズの効率性: collectAsState()はComposeの状態管理システムと密接に統合されており、変更があった部分のみ効率的に再コンポーズされます。

開発者の効率性: collectAsState()を使用すると、ボイラープレートコードを減らし、コアのビジネスロジックに集中することができます。

使用シナリオの比較

collectAsState()が適しているケース:

  • シンプルなUI状態を監視する場合
  • 複数の独立したFlowを監視する場合
  • 標準的なパターンに従うアプリで使用する場合
  • コードの簡潔さや可読性を重視する場合

collectAsState()を使用しない方が適しているケース:

  • 複雑なFlowの変換や結合が必要な場合
  • 特別なエラーハンドリングが必要な場合
  • Flowの収集に対して細かい制御が必要な場合
  • 特定のコルーチンスコープでFlowを収集する必要がある場合(ただし、collectAsState(context = ...)を使用することで対応可能)

結論

collectAsState()は、Jetpack ComposeでFlowを監視する最もシンプルかつ効率的な方法です。従来の方法と比較すると、コードがより簡潔になり、ライフサイクル管理が自動化され、複数のFlowを容易に処理できるというメリットがあります。

しかし、特別な要件がある場合には、従来の方法を使用してFlowの収集をより細かく制御することも可能です。それぞれの方法のメリット・デメリットを理解し、状況に応じて適切に選択することが重要です。


GitHub : https://github.com/GEUN-TAE-KIM/collectAsState_study

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?