KotlinのshareIn()はコールドFlowをホットFlowにすることができますが、
- SharedFlowを使用し、コレクターが存在する場合にのみデータを流したい
- 不要な処理を避け、パフォーマンスを最適化したい
といったケースでは、適切なフローの仕組みを選択する必要があります。
SharedFlowとは?
SharedFlow
は、複数のコレクターが同じデータを受け取れるホットFlowの一種です。しかし、デフォルトではコレクターの有無にかかわらずデータが流れ続けるため、「コレクターがいないときは値を流さない」ように制御したほうがいい場合もあります。
SharedFlowの基本
val coldFlow = flow {
(5) {
repeat(it)
emit(1000)
delay}
}
val sharedFlow = coldFlow.shareIn(
= this,
scope = SharingStarted.Eagerly,
started = 0
replay )
この場合sharedFlow
はコレクターがいなくても値を流し続けます。
コレクターが存在するときだけ値を流す方法
KotlinのSharedFlowでコレクターがいる場合のみデータを流し、いなくなったら停止するようにしたい場合、
shareIn
とSharingStarted.WhileSubscribed()
を組み合わせるのが有効です。
val sharedFlow = coldFlow.shareIn(
= coroutineScope,
scope = SharingStarted.WhileSubscribed(),
started = 0
replay )
この方法を使うとコレクターが1つ以上存在するときのみ値が流れ、0になると停止します。
WhileSubscribed()
のパラメータをカスタマイズ
.WhileSubscribed(
SharingStarted= 5000L, // コレクターがいなくなってから停止するまでの待機時間
stopTimeoutMillis = 0L // リプレイキャッシュの保持時間
replayExpirationMillis )
stopTimeoutMillis
: コレクターが0になってから何ミリ秒待つか(デフォルトは0ms)replayExpirationMillis
: リプレイデータの有効期間(デフォルトはLong.MAX_VALUE
)
例えばstopTimeoutMillis = 5000L
に設定すると、コレクターがいなくなっても5秒間はデータを流し続け、その後に完全停止します。
実際の活用例
AndroidアプリではRoomデータベースの変更をFlowで監視することがよくあります。SharedFlowを使い画面が開いている間だけデータを監視し、閉じたら自動的に停止する仕組みを作ることができます。
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getUsers(): Flow<List<User>>
}
class UserRepository(private val userDao: UserDao) {
private val userFlow = userDao.getUsers()
.shareIn(
= CoroutineScope(Dispatchers.IO),
scope = SharingStarted.WhileSubscribed(5000L),
started = 0
replay )
fun getSharedUsers(): Flow<List<User>> = userFlow
}
この場合、画面が表示されている間だけRoomデータベースの変更を監視し、閉じたら自動的に監視を停止できます。
まとめ
- SharedFlowはデフォルトではコレクターがいなくてもデータを流し続ける
- コレクターがいる間だけ値を流したい場合は
shareIn
+SharingStarted.WhileSubscribed()
を利用する stopTimeoutMillis
を使うと一定時間待機してから停止することも可能- Roomデータベースの監視に活用すると、画面が開いている間だけデータ変更を反映できる
この仕組みを使うことでSharedFlowをより効率的に活用できます。ぜひ実装に取り入れてみてください。