フローの概要

Kotlin バージョンの Home API は、フローを使用します。フローとは、変換、フィルタリング、マッピング、コレクションとの相互変換など、非同期データ ストリームを処理するための強力な機能を提供する Kotlin 言語の機能です。

コルーチンはフローとの関連性が高く、非同期コードを記述するための優雅な抽象化を提供します。これにより、デベロッパーはコールバック ルーティンを明示的に記述する必要がなくなります。フローはコルーチンと連携して機能し、関数やスレッドの完了を待たずに、コルーチンから非同期でデータを取得する方法として機能します。

データは、コンシューマが「収集」と呼ばれるプロセスでフローから取得します。Kotlin の Flow には、この目的に使用できる collect() 関数があります。収集中にフロー内で発生する処理は、フローのタイプによって異なります。

Kotlin Flow(Flow<T>)はデフォルトでコールドです。つまり、フロービルダー コードは collect() が呼び出されたときにのみデータを生成します。一方、ホット フローでは、データがすぐに生成され、キューのようにメモリにバッファリングされます。これにより、データが使用されるたびにデータが使用されます。ホットフローではデータがメモリ内に保持されるため、ステートフルと見なされます。

shareIn 演算子を使用して、フローをコールドからホットに変換できます(shareIn を使用したコールド Flow のホット化を参照)。SharedFlow または StateFlow を使用して、コールド Flow をホット Flow に変換することもできます。

サンプルアプリでのフローの使用方法

サンプルアプリは、Home API のフローに接続された Jetpack Compose のビューモデルを使用します。サンプルアプリの UI 要素の多くは状態によって駆動されますが、操作できます。また、このサンプルアプリでは、リアルタイムのユーザー エクスペリエンスを実現するために、デバイスの状態と UI の「楽観的」な状態を組み合わせています。オプティミスティックとは、アプリが特定のアクションが成功したと想定し、確認を待たずに期待される結果を反映するように UI をすぐに更新することを意味します。アクションが失敗した場合は、実際の状態を反映するように UI が更新されます。

サンプルアプリでは、ビューモデルのレイヤ(構造、部屋、デバイス)ごとにフローを作成する。たとえば、GlobalViewModel.kt で次の呼び出しがある構造に対して行われます。

  fun getStructuresState(): StateFlow<List<StructureModel>?> =
    homeClient
      .structures()
      .map { structures -> structures.map { StructureModel(it.name, it) }.sortedBy { it.name } }
      .handleErrors()
      .flowOn(Dispatchers.IO)
      .stateIn(scope = viewModelScope, started = SharingStarted.WhileSubscribed(), null)

emitAll() は、指定されたフローからすべての値を収集し、コレクタに出力します。stateIn() は、アップストリーム フローの単一の実行中のインスタンスから出力された最新の値を複数のダウンストリーム サブスクライバーと共有します。詳細については、kotlinx.coroutines.flow のリファレンスをご覧ください。

Jetpack Compose

フロー オブジェクトをローカルメモリに保存して終了しないようにするには、Kotlin の remember API を使用します。

Jetpack Compose でこれを collectAsStateWithLifecycle() とともに使用すると、その状態を示すアプリ UI が実際にフォアグラウンドにあるかどうかに基づいて、Jetpack が Flow への登録と登録解除を自動的に管理します。

サンプルアプリでは、簡単な呼び出しでこの処理を行います。前述の getStructuresState() 関数を使用すると、次のようになります。

val structuresList by
    remember(globalViewModel) { globalViewModel.getStructuresState() }.collectAsStateWithLifecycle()

これで、構造の状態(name など)が変更されると、それを使用するコンポーザブル関数に更新された状態が自動的に反映されます。サンプルアプリでは、これは HomeActivityContent() 関数です。

リソース

Kotlin、Flow、コルーチン、Jetpack Compose の詳細については、次のリソースをご覧ください。