[TOC]
`SharedFlow` 和 `StateFlow` 是 Kotlin Coroutines 庫提供的兩種流類型,它們都用于管理和處理流數據,但在使用場景和特性上有所不同。
## flowOn切換線程
### 協程在IO線程
```kotlin
private fun testFlowOn() {
lifecycleScope.launch(Dispatchers.IO) {
flow {
Log.d("TAGTAG", "emit ${Thread.currentThread().name}")
emit(1)
}.onStart {
Log.d("TAGTAG", "onStart ${Thread.currentThread().name}")
}.flowOn(Dispatchers.Main).onEach {
Log.d("TAGTAG", "onEach ${Thread.currentThread().name}")
}.flowOn(Dispatchers.IO).onCompletion {
Log.d("TAGTAG", "onCompletion ${Thread.currentThread().name}")
}.collect {
Log.d("TAGTAG", "collect ${Thread.currentThread().name}")
}
}
}
```
輸出:
```log
14:44:31.038 D onStart main
14:44:31.038 D emit main
14:44:31.039 D onEach DefaultDispatcher-worker-1
14:44:31.039 D collect DefaultDispatcher-worker-1
14:44:31.044 D onCompletion DefaultDispatcher-worker-1
```
### 協程在主線程
```kotlin
private fun testFlowOn() {
lifecycleScope.launch(Dispatchers.Main) {
flow {
Log.d("TAGTAG", "emit ${Thread.currentThread().name}")
emit(1)
}.onStart {
Log.d("TAGTAG", "onStart ${Thread.currentThread().name}")
}.flowOn(Dispatchers.Main).onEach {
Log.d("TAGTAG", "onEach ${Thread.currentThread().name}")
}.flowOn(Dispatchers.IO).onCompletion {
Log.d("TAGTAG", "onCompletion ${Thread.currentThread().name}")
}.collect {
Log.d("TAGTAG", "collect ${Thread.currentThread().name}")
}
}
}
```
輸出結果
```log
14:46:27.449 D onStart main
14:46:27.449 D emit main
14:46:27.451 D onEach DefaultDispatcher-worker-1
14:46:27.453 D collect main
14:46:27.454 D onCompletion main
```
### 總結
1. `flowOn`操作符對上游范圍有效, 范圍是指兩個`flowOn`之間,如果只有一個`flowOn`,則上游全部有效;
2. 最后一個`flowOn`后的操作所在線程與當前整個`flow`所在的線程池相同,即 `collect`和`onCompletion`的執行線程只與所啟動的協程所在的線程有關;
## Flow、StateFlow、SharedFlow
### 熱流與冷流
**熱流**和**冷流**是關于數據流的兩個基本概念,它們描述了數據流何時開始以及如何傳遞事件的方式。
- **熱流**是一種主動的數據流。它在創建時就開始發射事件,無論是否有觀察者訂閱。即使沒有觀察者,熱流也會持續產生事件。當觀察者訂閱時,它只是加入了已經運行的數據流,開始接收當前已經產生的事件。
- **冷流**是一種被動的數據流。它在有觀察者訂閱時才開始發射事件。每個觀察者都會獲得相同的事件序列,而不會受到其他觀察者的影響。
`SharedFlow` 和 `StateFlow`都是熱流。即沒有觀察者,數據會持續更新,與`LiveData`類似。 其中`MutableSharedFlow`與`MutableStateFlow`是它們的可變類型。
### `StateFlow`
#### 特性
* **持久狀態**:`StateFlow` 總是持有最新的狀態值,并且當新的觀察者(collector)開始收集時,它會立即收到當前的狀態。
* **可變性**:`StateFlow` 是可變的,可以通過 `.value` 屬性進行讀取和更新。
* **廣播給所有觀察者**:每次更新值時,`StateFlow` 會廣播最新的狀態給所有當前的觀察者。
* **`Replay` 緩存**:`StateFlow` 總是將最新的狀態重播給新觀察者,不需要設置重播緩存大小。
#### 使用場景
* **UI 狀態管理**:適用于需要持續更新和持有最新狀態值的場景,例如在 ViewModel 中管理 UI 狀態。
* **單一來源的狀態更新**:適用于只有一個數據源負責更新狀態的場景。
```kotlin
private val _state = MutableStateFlow("Initial State")
val state: StateFlow<String> get() = _state
fun updateState(newState: String) {
_state.value = newState
}
// 觀察者
viewModel.state.collect { newState ->
// 更新UI
}
```
#### MutableStateFlow
`MutableStateFlow`的構造函數有一個默認參數,即初始狀態值。以下是`MutableStateFlow`構造函數:
```kotlin
public fun <T> MutableStateFlow(value: T): MutableStateFlow<T> = StateFlowImpl(value ?: NULL)
```
構造函數中的`value`參數表示`MutableStateFlow`的初始狀態值。在創建`MutableStateFlow`時,需要提供這個初始狀態值。
### `SharedFlow`
#### 特性
* **多播**:`SharedFlow` 支持多播,可以有多個同時活躍的觀察者。
* **無初始值**:與 `StateFlow` 不同,`SharedFlow` 不持有初始值。
* **重播緩存**:可以配置重播緩存的大小,以便新觀察者能夠接收到一定數量的最近發射的值。
* **完全控制**:相比于 `StateFlow`,`SharedFlow` 提供了更多的控制,例如重播緩沖區大小、過期策略等。
#### 使用場景
* **事件流**:適用于需要發送一次性事件的場景,例如導航事件、錯誤消息等。
* **廣播消息**:適用于需要廣播消息給多個觀察者的場景,且這些消息不需要持久化狀態。
```kotlin
private val _events = MutableSharedFlow<String>(replay = 2)
val events: SharedFlow<String> get() = _events
fun sendEvent(event: String) {
viewModelScope.launch {
_events.emit(event)
}
}
// 觀察者
viewModel.events.collect { event ->
// 處理事件
}
```
#### MutableSharedFlow
**MutableSharedFlow**是一種可變的、用于創建共享流的類。下面是MutableSharedFlow的一些主要構造函數參數及其默認值:
```kotlin
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
) : MutableSharedFlow<T> { /*...*/ }
```
1. `replay`: 表示在訂閱時從流中回放的元素數量。默認值為 0,表示不回放任何元素。如果設置為正整數 n,則在訂閱時將向新訂閱者回放最近的 n 個元素。
2. `extraBufferCapacity`: 表示額外的緩沖容量,用于存儲訂閱者尚未消耗的元素。默認值為 0,表示不使用額外的緩沖容量。設置為正整數 m 時,會在內部使用一個帶有額外 m 容量的緩沖區。
3. `onBufferOverflow`: 表示在緩沖區溢出時的處理策略。默認值為 BufferOverflow.SUSPEND,表示當緩沖區溢出時暫停發射,等待訂閱者消費。其他選項還包括 BufferOverflow.DROP_OLDEST 和 BufferOverflow.DROP_LATEST,它們分別表示在緩沖區溢出時丟棄最老的元素或最新的元素。
### 如何選擇
1. **需要持久狀態且最新值可供新觀察者立即獲取**:
* 使用 `StateFlow`。它適用于需要始終持有最新狀態的場景,如 UI 狀態管理。
2. **需要廣播事件且事件不需要持久化**:
* 使用 `SharedFlow`。它適用于一次性事件流或廣播消息的場景。
3. **多個觀察者同時接收同樣的數據**:
* `SharedFlow` 提供了多播能力,并且可以配置重播緩存以滿足需求。
### 總結
* **`StateFlow`** 是一種特別適用于狀態管理的流,它始終持有最新的狀態值,適合單一數據源更新的場景。
* **`SharedFlow`** 提供了更靈活的事件處理能力,支持多播和重播緩存,適合處理一次性事件或廣播消息。
- 寫在前面的話
- Java
- 基礎
- Double的比較
- 小數怎么用二進制表示
- 多線程
- 并發和并行
- 線程池
- 線程池背景
- 線程池構造
- 任務阻塞隊列
- Flutter
- 基礎知識
- Dart基礎
- Android
- 項目架構
- View
- 非UI線程更新View
- AlarmManager
- 對比postDelaryed和Timer
- Bitmap
- 加載100M的圖片卻不撐爆內存
- Bitmap壓縮
- Bitmap局部解碼
- 計算圖片的內存占用
- Android動畫
- Android動畫類型
- Android動畫原理
- 屬性動畫
- 幀動畫
- 補間動畫
- 使用動畫的注意事項
- Android新特性
- 權限組
- Android23(Marshmallow)-6.0
- Android24(Nougat)-7.0
- Android26(Oreo)-8.0
- Android28(Pie)-9.0
- Android29(Q)-10.0
- AndroidX遷移
- Kotlin
- 關鍵字
- Kotlin操作符
- CoroutineScope
- Flow
- CoroutineException