一 sharedFlow是什么
SharedFlow
是 Kotlin 協程中 Flow
的一種 熱流(Hot Flow),用于在多個訂閱者之間 共享事件或數據流。它適合處理 一次性事件(如導航、彈窗、Toast、刷新通知等),而不是持續狀態。
? SharedFlow 是什么?
SharedFlow
是 Flow
的一種擴展,具備以下特點:
特性 | 描述 |
---|---|
熱流 | 一旦被觸發,即使沒人監聽也會發出 |
多訂閱者 | 所有活躍訂閱者都能收到事件 |
不保留最新值(除非設置 replay) | 不像 StateFlow 那樣始終有個當前值 |
可配置 buffer 和回放(replay) | 控制事件是否緩存、丟棄或排隊 |
它本質上是一個“事件廣播器”。
? 常見使用場景
📍 1. 一次性 UI 事件
- Toast 彈窗
- SnackBar 提示
- 導航跳轉
- 關閉頁面
- 對話框展示/取消
這些事件都是“一次性的”,不需要保存狀態,也不該重復觸發,因此適合 SharedFlow
。
sealed class UiEvent {data class ShowToast(val message: String) : UiEvent()object NavigateToHome : UiEvent()
}
// ViewModel 中
private val _uiEvent = MutableSharedFlow<UiEvent>()
val uiEvent = _uiEvent.asSharedFlow()fun loginSuccess() {viewModelScope.launch {_uiEvent.emit(UiEvent.NavigateToHome)}
}
// Fragment 中
lifecycleScope.launchWhenStarted {viewModel.uiEvent.collect { event ->when (event) {is UiEvent.ShowToast -> showToast(event.message)is UiEvent.NavigateToHome -> navigate()}}
}
📍 2. 流式通知
- 通知其他模塊刷新數據
- 數據拉取完成通知頁面更新
- ViewModel 向 UI 發信號
📍 3. 替代 LiveData<Event>
解決重復消費問題
傳統用 LiveData<Event<T>>
或 SingleLiveEvent
處理一次性事件,代碼復雜、不優雅,而 SharedFlow
是官方推薦的替代方案。
? 在公司項目中,SharedFlow 的典型用法
層 | 使用方式 |
---|---|
ViewModel | 使用 MutableSharedFlow 發送事件 |
UI(Activity/Fragment) | 使用 collect 監聽事件,做 UI 響應 |
工具類/中間層 | 也可以用于廣播通知、分發事件 |
示例:登錄成功后跳轉 + 彈出 Toast
// ViewModel
val eventFlow = MutableSharedFlow<UiEvent>()suspend fun login(username: String, pwd: String) {if (doLogin(username, pwd)) {eventFlow.emit(UiEvent.ShowToast("登錄成功"))eventFlow.emit(UiEvent.NavigateHome)}
}
? 與其他 Flow 類型的對比
類型 | 是否熱流 | 是否可變 | 是否保留值 | 場景 |
---|---|---|---|---|
Flow | ? 冷流 | ? | ? | 一次性數據流 |
SharedFlow | ? 熱流 | ? | ?(可設置 replay) | 一次性事件廣播 |
StateFlow | ? 熱流 | ? | ?(必須初始值) | 狀態管理(UI 狀態、進度等) |
? 總結一句話:
SharedFlow
= Kotlin 中處理一次性事件的推薦工具,適合在 ViewModel → UI 層傳遞 Toast、導航、彈窗等短暫行為,比LiveData
更現代、可控。
二 sharedFlow如何處理背壓的?
? 1. SharedFlow 是如何處理被壓的(Backpressure)?
SharedFlow
是 熱流(hot stream),意味著數據會立即發出,而不會像 Flow
那樣等待訂閱者 collect。這也就意味著:
- 如果發射得太快(比如連續多次 emit)
- 而訂閱者還沒來得及 collect
- 數據就可能被丟棄,或緩存起來等待
這就需要一個“緩存策略”來決定怎么處理這些“來不及處理”的數據 —— 這就是 SharedFlow 的 buffer 和 溢出策略(onBufferOverflow)。
? 2. replay = 2, extraBufferCapacity = 5
的含義
val sharedFlow = MutableSharedFlow<Int>(replay = 2,extraBufferCapacity = 5
)
這兩者分別控制了兩塊緩存區域:
參數 | 含義 |
---|---|
replay = 2 | 每個新訂閱者會 立刻收到前 2 條值(即“回放值”) |
extraBufferCapacity = 5 | 除了 replay 緩沖區之外,還允許臨時緩存 最多 5 條數據 |
💡 總緩沖區大小 = replay + extraBufferCapacity
即上面的配置,總共可以緩沖 最多 7 條數據。
這意味著在沒有 collect 的情況下,可以最多 emit 7 條數據不會失敗或丟失。
? 3. onBufferOverflow = DROP_OLDEST / DROP_LATEST / SUSPEND
是什么?
這是控制當 緩沖區已滿時,繼續 emit 會怎么處理的策略。
支持的策略:
策略名 | 解釋 |
---|---|
DROP_OLDEST | 丟掉最早 emit 的一條數據(先進先出) |
DROP_LATEST | 丟掉新發射的數據(調用的 emit) |
SUSPEND (默認) | 掛起 emit 調用,直到 buffer 有空間(安全但可能阻塞) |
示例說明:
val flow = MutableSharedFlow<Int>(replay = 1,extraBufferCapacity = 2,onBufferOverflow = BufferOverflow.DROP_OLDEST
)
此時總 buffer 是 3 條:
- 如果連續 emit 第 1、2、3 條 → 都能進 buffer。
- 如果 emit 第 4 條時還沒人 collect → buffer 滿了。
DROP_OLDEST
策略:會把 第 1 條值移除,保留 2、3、4。
🔄 emit() 和 tryEmit() 的區別:
emit()
是 掛起函數,可能會suspend
(如果 buffer 滿且策略是SUSPEND
)。tryEmit()
是 非掛起,返回true/false
表示是否成功發射。
? 總結一句話:
配置項 | 意義 |
---|---|
replay | 新訂閱者能收到多少“歷史值” |
extraBufferCapacity | 在未 collect 情況下,能暫存多少新值 |
onBufferOverflow | 當緩存已滿,是否丟老的、丟新的,或掛起等候 |
總緩存 | replay + extraBufferCapacity 條數據 |