FlowオペレータshareInとstateInの使用上の注意事項
8544 ワード
Flow.shareInおよびFlow.stateInオペレータは、上流のコールドデータストリームからの情報を複数の収集者にブロードキャストすることができるコールドストリームをホットストリームに変換することができる.この2つのオペレータは、通常、パフォーマンスを向上させるために使用されます.コレクタがない場合にバッファを追加します.あるいはいっそキャッシュメカニズムとして使用します.
に注意
:コールドフローは必要に応じて作成され、観察されたときにデータが送信されます.熱流は常に活発であり,観察されるかどうかにかかわらずデータを送信することができる.
ここでは、shareInオペレータとstateInオペレータの例を示します.特定のインスタンスに対して構成し、一般的なトラップを回避する方法を学びます.
最下位データ・ストリーム・プロダクション
私の前の記事で使用した例を引き続き使用します.下位データストリーム生産者を使用して位置更新を発行します.callbackFlowを用いて実現された冷流である.各新しいコレクターは、データ・ストリームの生産者コード・ブロックをトリガーし、FusedLocationProviderClientに新しいコールバックを追加します.
異なる例でshareInとstateInを使用してlocationsSourceデータストリームを最適化する方法を見てみましょう.
ShareInかstateInか?
私たちが議論する最初の話題は
注意:詳細はこちら
デルのドキュメント .
StateFlowはSharedFlowの特殊な構成であり、最後に送信されたアイテムは新しいコレクターに再送信され、これらのアイテムはAny.equalsを使用してマージされます.詳細については、StateFlowドキュメントで確認できます.
両者の最も主要な違いは、
パフォーマンスの向上
これらのAPIは、必要に応じて同じデータ・ストリームの新しいインスタンスを作成するのではなく、すべての収集者が観察する同じデータ・ストリーム・インスタンスを共有することで、パフォーマンスを向上させることができます.
次の例では、
WhileSubscribed共有ポリシーは、収集者がいないときに上流データストリームをキャンセルするために使用される.これにより、プログラムが位置更新に興味を持っていないときにリソースの浪費を避けることができます.
Androidアプリで注意!ほとんどの場合、
WhileSubscribed(5000)は、最後の収集者が消えた後、上流データストリームのアクティブな状態を5秒間維持する.これにより、構成の変更など、特定の状況下で上流データストリームの再起動を回避できます.このテクニックは、上流データストリームの作成コストが高い場合や、ViewModelでこれらのオペレータを使用する場合に特に役立ちます.
バッファイベント
次の例では、私たちのニーズが変わりました.バックグラウンドからフロントに戻ると、最後の10の場所が画面に表示されるように、リスニング位置の更新を維持する必要があります.
パラメータ
データのキャッシュ
デルのニーズは再び変化し、今回はバックグラウンドで監視位置の更新を継続する必要はありません.ただし、最後に送信されたアイテムをキャッシュして、データが古い場合でも、現在の場所を取得するときに画面にデータを表示できるようにする必要があります.この場合、stateInオペレータを使用できます.
注意!各関数呼び出し時に新しいインスタンスを作成しないでください
関数呼び出しの戻りを呼び出すときにshareInまたはstateInを使用して新しいデータストリームを作成しないでください.これにより、関数呼び出しのたびに新しいSharedFlowまたはStateFlowが作成され、役割ドメインがキャンセルされるか、参照がないときにゴミが回収されるまでメモリに保持されます.
パラメータが必要なデータ・ストリーム
この例を最適化する方法は、アプリケーションのニーズに応じて異なります.複数のユーザーから同時にイベントを受信できますか?答えが肯定的である場合は、 ユーザーが1人しか許可されておらず、コレクターが新しいユーザーを観察するために更新する必要がある場合は、すべてのコレクターで共通の
に注意
:コールドフローは必要に応じて作成され、観察されたときにデータが送信されます.熱流は常に活発であり,観察されるかどうかにかかわらずデータを送信することができる.
ここでは、shareInオペレータとstateInオペレータの例を示します.特定のインスタンスに対して構成し、一般的なトラップを回避する方法を学びます.
最下位データ・ストリーム・プロダクション
私の前の記事で使用した例を引き続き使用します.下位データストリーム生産者を使用して位置更新を発行します.callbackFlowを用いて実現された冷流である.各新しいコレクターは、データ・ストリームの生産者コード・ブロックをトリガーし、FusedLocationProviderClientに新しいコールバックを追加します.
class LocationDataSource(
private val locationClient: FusedLocationProviderClient
) {
val locationsSource: Flow = callbackFlow {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
result ?: return
try { offer(result.lastLocation) } catch(e: Exception) {}
}
}
requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())
.addOnFailureListener { e ->
close(e) // in case of exception, close the Flow
}
// Flow
awaitClose {
removeLocationUpdates(callback)
}
}
}
異なる例でshareInとstateInを使用してlocationsSourceデータストリームを最適化する方法を見てみましょう.
ShareInかstateInか?
私たちが議論する最初の話題は
shareIn
とstateIn
の違いです.shareIn
オペレータはSharedFlowを返し、stateIn
はStateFlowを返します.注意:詳細はこちら
StateFlow
とSharedFlow
の詳細は、デルのドキュメント .
StateFlowはSharedFlowの特殊な構成であり、最後に送信されたアイテムは新しいコレクターに再送信され、これらのアイテムはAny.equalsを使用してマージされます.詳細については、StateFlowドキュメントで確認できます.
両者の最も主要な違いは、
StateFlow
インタフェースで、value
プロパティを読み込むことで、最後に発行された値に同期してアクセスできることです.これはSharedFlow
の使用方法ではありません.パフォーマンスの向上
これらのAPIは、必要に応じて同じデータ・ストリームの新しいインスタンスを作成するのではなく、すべての収集者が観察する同じデータ・ストリーム・インスタンスを共有することで、パフォーマンスを向上させることができます.
次の例では、
LocationRepository
は、LocationDataSource
によって暴露されたlocationsSource
データストリームを消費し、shareInオペレータを使用して、ユーザ位置情報に関心のある収集者ごとに同じデータストリームインスタンスからデータを収集する.ここでは、locationsSource
のデータ・ストリーム・インスタンスのみが作成され、すべてのコレクターによって共有されます.class LocationRepository(
private val locationDataSource: LocationDataSource,
private val externalScope: CoroutineScope
) {
val locations: Flow =
locationDataSource.locationsSource.shareIn(externalScope, WhileSubscribed())
}
WhileSubscribed共有ポリシーは、収集者がいないときに上流データストリームをキャンセルするために使用される.これにより、プログラムが位置更新に興味を持っていないときにリソースの浪費を避けることができます.
Androidアプリで注意!ほとんどの場合、
WhileSubscribed(5000)は、最後の収集者が消えた後、上流データストリームのアクティブな状態を5秒間維持する.これにより、構成の変更など、特定の状況下で上流データストリームの再起動を回避できます.このテクニックは、上流データストリームの作成コストが高い場合や、ViewModelでこれらのオペレータを使用する場合に特に役立ちます.
バッファイベント
次の例では、私たちのニーズが変わりました.バックグラウンドからフロントに戻ると、最後の10の場所が画面に表示されるように、リスニング位置の更新を維持する必要があります.
class LocationRepository(
private val locationDataSource: LocationDataSource,
private val externalScope: CoroutineScope
) {
val locations: Flow =
locationDataSource.locationsSource
.shareIn(externalScope, SharingStarted.Eagerly, replay = 10)
}
パラメータ
replay
の値を10に設定して、最後に発行された10個のアイテムをメモリに保持し、収集者がデータストリームを観察するたびに再送信します.内部データストリームが常にアクティブであることを維持し、位置更新を送信するために、共有ポリシーSharingStarted.Eagerly
を使用し、収集者がいなくても更新を常に傍受することができます.データのキャッシュ
デルのニーズは再び変化し、今回はバックグラウンドで監視位置の更新を継続する必要はありません.ただし、最後に送信されたアイテムをキャッシュして、データが古い場合でも、現在の場所を取得するときに画面にデータを表示できるようにする必要があります.この場合、stateInオペレータを使用できます.
class LocationRepository(
private val locationDataSource: LocationDataSource,
private val externalScope: CoroutineScope
) {
val locations: Flow =
locationDataSource.locationsSource.stateIn(externalScope, WhileSubscribed(), EmptyLocation)
}
Flow.stateIn
は、最後に送信されたアイテムをキャッシュし、新しいコレクターに再生することができる.注意!各関数呼び出し時に新しいインスタンスを作成しないでください
関数呼び出しの戻りを呼び出すときにshareInまたはstateInを使用して新しいデータストリームを作成しないでください.これにより、関数呼び出しのたびに新しいSharedFlowまたはStateFlowが作成され、役割ドメインがキャンセルされるか、参照がないときにゴミが回収されるまでメモリに保持されます.
class UserRepository(
private val userLocalDataSource: UserLocalDataSource,
private val externalScope: CoroutineScope
) {
// shareIn stateIn
// SharedFlow StateFlow, 。
fun getUser(): Flow =
userLocalDataSource.getUser()
.shareIn(externalScope, WhileSubscribed())
// shareIn stateIn
val user: Flow =
userLocalDataSource.getUser().shareIn(externalScope, WhileSubscribed())
}
パラメータが必要なデータ・ストリーム
userId
などのパラメータを必要とするデータストリームは、shareIn
またはstateIn
を単純に使用して共有することはできない.オープンソースプロジェクトであるGoogle I/OのAndroidアプリケーションioschedを例にとると、ソース中からユーザイベントを取得するデータストリームはcallbackFlow
によって実現されていることがわかります.パラメータとしてuserId
を受信するため、shareIn
またはstateIn
オペレータを単純に多重化することはできない.class UserRepository(
private val userEventsDataSource: FirestoreUserEventDataSource
) {
// Firestore 。
// `userId`,
// shareIn stateIn .
// , SharedFlow StateFlow
fun getUserEvents(userId: String): Flow =
userLocalDataSource.getObservableUserEvents(userId)
}
この例を最適化する方法は、アプリケーションのニーズに応じて異なります.
SharedFlow
またはStateFlow
インスタンスにmapを作成し、subscriptionCount
が0の場合、参照を削除して上流データストリームを終了する必要があります.SharedFlow
またはStateFlow
にイベント更新を送信し、クラス内の変数として共通データストリームを送信できます.shareIn
およびstateIn
オペレータは、冷却フローとともに使用してパフォーマンスを向上させ、コレクタがない場合にバッファを追加したり、キャッシュメカニズムとして直接使用したりすることができます.関数呼び出しのたびに新しいデータ・ストリーム・インスタンスを作成しないでください.これにより、リソースの浪費や予想外の問題が発生します.