Android 可觀測數據流(StateFlow 與 SharedFlow)
上一章介紹的 Flow 是「冷流」(Cold Stream),只有在被收集時才會執行。但在 Android 開發中,我們常需要「熱流」(Hot Stream):無論是否有訂閱者,資料流都可能持續活躍,或者保留最新的狀態給新的訂閱者。
這就是 StateFlow 與 SharedFlow 的用途。它們是用來取代 LiveData 的現代化方案。
StateFlow:狀態持有者
StateFlow 是一個專門用來持有單一、最新狀態的熱流。它非常適合用來實作 MVVM 架構中的 ViewModel 狀態。
特性
- 永遠有值:必須提供初始值。
- 防抖動 (Conflated):如果新舊值相同 (
enabled == enabled),不會發射更新。 - 熱流:只要 Scope 存在,它就保持活躍。
在 ViewModel 中使用
class MainViewModel : ViewModel() {
// 內部使用 MutableStateFlow 來更新狀態
private val _uiState = MutableStateFlow(UiState.Loading)
// 對外暴露唯讀的 StateFlow
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
val data = repository.fetchData()
_uiState.value = UiState.Success(data) // 更新狀態
}
}
}
在 UI (Compose) 中觀察
在 Jetpack Compose 中,我們使用 collectAsStateWithLifecycle (推薦) 或 collectAsState 來觀察 StateFlow。
@Composable
fun MainScreen(viewModel: MainViewModel) {
// 將 StateFlow 轉換為 Compose 的 State
val state by viewModel.uiState.collectAsStateWithLifecycle()
when (state) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Success -> Text("Data: ${(state as UiState.Success).data}")
}
}
collectAsStateWithLifecycle 需要依賴 androidx.lifecycle:lifecycle-runtime-compose 套件。它會自動在 App 進入背景時停止收集,節省資源。SharedFlow:事件流
SharedFlow 用於處理一次性事件 (One-shot Events),例如:顯示 Toast、導航跳轉、Snackbar 提示。
特性
- 沒有初始值。
- 可以重播 (Replay):設定
replay參數,讓新的訂閱者也能收到之前的 n 個事件。 - 多對多:一個 SharedFlow 可以有多個發射者與收集者。
在 ViewModel 中使用
class MainViewModel : ViewModel() {
private val _events = MutableSharedFlow<String>()
val events = _events.asSharedFlow()
fun showMessage(msg: String) {
viewModelScope.launch {
_events.emit(msg) // 發送事件
}
}
}
在 UI 中觀察
因為是單次事件,我們通常在 LaunchedEffect 中收集:
@Composable
fun MainScreen(viewModel: MainViewModel) {
val context = LocalContext.current
LaunchedEffect(Unit) {
viewModel.events.collect { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
}
}
StateFlow vs SharedFlow vs LiveData
| 特性 | StateFlow | SharedFlow | LiveData |
|---|---|---|---|
| 初始值 | 必須有 (Always has value) | 無 (No initial value) | 可有可無 |
| 適用場景 | UI 狀態 (UI State) | 單次事件 (Events) | UI 狀態 (舊專案) |
| 重複值處理 | 自動過濾相同值 (Distinct) | 不過濾 | 不過濾 |
| 生命週期感知 | 需搭配 flowWithLifecycle | 需搭配 flowWithLifecycle | 自動感知 |
| 執行緒 | 任何 Coroutine Context | 任何 Coroutine Context | 僅限主執行緒 |
結論
- UI 狀態:請使用
StateFlow。它完美替代了 LiveData,並且與 Coroutines 生態系整合得更好。 - 一次性事件:請使用
SharedFlow。這解決了 LiveData "SingleLiveEvent" 的長期痛點。 - 資料層:請使用普通的
Flow(Cold Stream)。
掌握這三種流,你就能構建出響應式、高效且架構清晰的 Android 應用程式。