0
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?

Kotlin Flow 実践メモ:StateFlow の `.value` と `.emit()` の使い分け

Posted at

Kotlin Flow 実践メモ:StateFlow の .value.emit() の使い分け

Kotlin の StateFlow を使っていると、
「値をセットするときは .emit() を使うべき?」という疑問に出会います。

結論から言えば、使い分けが必要です。
特に通信処理やUI更新などで誤信号を防ぎたいとき、
この区別を理解しておくことが重要です。


⚙️ .value.emit() の違い

項目 .value .emit()
使用場所 通常関数・非suspend関数でも可 suspend関数内でのみ可
更新タイミング 即時(同期的) suspendにより順序制御される
戻り動作 単なる代入 suspend処理として一時停止
適用対象 MutableStateFlow MutableStateFlow / MutableSharedFlow
主な用途 状態更新(単発) イベント通知(連続)

🧩 ① .value の使いどころ

MutableStateFlow の値を即座に更新したいときに使います。
通信結果やUI状態など、「最新値だけ保持」したい場合に最適です。

private val _state = MutableStateFlow("初期値")
val state: StateFlow<String> = _state

fun updateValue(newValue: String) {
    _state.value = newValue   // 即時反映、シンプルで安全
}
  • スレッドセーフ
  • 即時反映
  • suspend不要

💡 単発通信の結果など、1回だけ確定的に更新したいときに最適です。


🧭 ② .emit() の使いどころ

emit()suspend関数の中で逐次的に値を流すための関数です。
イベントや進捗など、“連続的な流れ”を扱う場合に向いています。

private val _state = MutableStateFlow("初期値")
val state: StateFlow<String> = _state

suspend fun fetchData() {
    _state.emit("通信開始")
    delay(1000)
    _state.emit("通信完了")
}
  • suspend関数のため、順序が保証される
  • Flowの「流れの中で値を出す」用途に最適

💡 進捗表示・連続イベント通知など、複数段階で値を送るときに使います。


⚡ 通信処理の実践例(誤信号防止)

単発通信では、emit() よりも .value = ... の方が安全で高速です。
以下のように書くと、「1発通信・1発通知」になります。

class TcpRepository {
    private val _responseFlow = MutableStateFlow<String?>(null)
    val responseFlow: StateFlow<String?> = _responseFlow

    suspend fun sendAndReceive(request: String) {
        withContext(Dispatchers.IO) {
            val socket = Socket("192.168.1.10", 4000)
            socket.getOutputStream().write(request.toByteArray(Charsets.SJIS))

            val buffer = ByteArray(1024)
            val len = socket.getInputStream().read(buffer)
            val response = buffer.copyOf(len).toString(Charsets.SJIS)

            _responseFlow.value = response   // ← emitではなくvalueで即時反映
            socket.close()
        }
    }
}

🔸 emit() を使うと suspend のタイミングで競合が起きやすく、
通信結果が複数回発火することがあります。
「1発通信→1通知」にしたい場合は .value 一択です。


🧱 ③ Eventラッパとの併用(再通知防止)

StateFlow は最新値を保持するため、同じ値が再通知される場合があります。
その防止には「イベントラッパ」を使います。

data class Event<out T>(private val content: T) {
    private var handled = false
    fun getIfNotHandled(): T? =
        if (handled) null else content.also { handled = true }
}

ViewModel側:

private val _eventFlow = MutableStateFlow<Event<String>?>(null)
val eventFlow = _eventFlow.asStateFlow()

fun notifyOnce(msg: String) {
    _eventFlow.value = Event(msg)
}

UI側:

viewModel.eventFlow.collect { event ->
    event?.getIfNotHandled()?.let { showToast(it) }
}

✅ まとめ:使い分けの指針

処理内容 推奨メソッド 理由
単発通信・UI状態更新 .value = ... 即時反映・誤信号防止
進捗通知・イベント配信 .emit(...) suspendで順序保証
SharedFlowイベント送信 .emit(...) 逐次配信
ViewModelの状態保持 .value 最新値を常に保持

💬 まとめの一言

Flow は「通信の通路」ではなく「状態の流れ」を扱うツールです。
通信(I/O)は suspend、通知は Flow。
この責務分離こそが、安定した通信設計と誤信号防止の鍵になります。


✍️ 書き手メモ
1発通信で誤信号が出ないようにするには、
“Flowをリアルタイム通知の手段として使いすぎない” こと。
.value は「即時に結果を反映するスイッチ」として非常に有効です。

0
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
0
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?