2025年3月1日土曜日

KotlinのSharedFlowで、コレクターが存在するときだけ値を流す方法

KotlinのshareIn()はコールドFlowをホットFlowにすることができますが、

  • SharedFlowを使用し、コレクターが存在する場合にのみデータを流したい
  • 不要な処理を避け、パフォーマンスを最適化したい

といったケースでは、適切なフローの仕組みを選択する必要があります。

SharedFlowとは?

SharedFlowは、複数のコレクターが同じデータを受け取れるホットFlowの一種です。しかし、デフォルトではコレクターの有無にかかわらずデータが流れ続けるため、「コレクターがいないときは値を流さない」ように制御したほうがいい場合もあります。

SharedFlowの基本

val coldFlow = flow {
    repeat(5) {
        emit(it)
        delay(1000)
    }
}

val sharedFlow = coldFlow.shareIn(
    scope = this,
    started = SharingStarted.Eagerly,
    replay = 0
)

この場合sharedFlowはコレクターがいなくても値を流し続けます。

コレクターが存在するときだけ値を流す方法

KotlinのSharedFlowでコレクターがいる場合のみデータを流し、いなくなったら停止するようにしたい場合、 shareInSharingStarted.WhileSubscribed()を組み合わせるのが有効です。

val sharedFlow = coldFlow.shareIn(
    scope = coroutineScope,
    started = SharingStarted.WhileSubscribed(),
    replay = 0
)

この方法を使うとコレクターが1つ以上存在するときのみ値が流れ、0になると停止します。

WhileSubscribed() のパラメータをカスタマイズ

SharingStarted.WhileSubscribed(
    stopTimeoutMillis = 5000L,    // コレクターがいなくなってから停止するまでの待機時間
    replayExpirationMillis = 0L   // リプレイキャッシュの保持時間
)
  • 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(
            scope = CoroutineScope(Dispatchers.IO),
            started = SharingStarted.WhileSubscribed(5000L),
            replay = 0
        )

    fun getSharedUsers(): Flow<List<User>> = userFlow
}

この場合、画面が表示されている間だけRoomデータベースの変更を監視し、閉じたら自動的に監視を停止できます。

まとめ

  • SharedFlowはデフォルトではコレクターがいなくてもデータを流し続ける
  • コレクターがいる間だけ値を流したい場合は shareIn + SharingStarted.WhileSubscribed() を利用する
  • stopTimeoutMillis を使うと一定時間待機してから停止することも可能
  • Roomデータベースの監視に活用すると、画面が開いている間だけデータ変更を反映できる

この仕組みを使うことでSharedFlowをより効率的に活用できます。ぜひ実装に取り入れてみてください。