在協(xié)程中,F(xiàn)low 是一種可以順序發(fā)出多個值的類型,而不是只返回單個值的掛起函數(shù)。例如,你可以使用 Flow 從數(shù)據(jù)庫接收實時更新。
數(shù)據(jù)流建立在協(xié)程之上,可以提供多個值。Flow 在概念上是可以異步計算的數(shù)據(jù)流。發(fā)出的值必須是同一類型。例如, Flow<Int>
是一個發(fā)出整數(shù)值的流。
數(shù)據(jù)流與生成一組序列值的 Iterator
非常相似,但它使用掛起函數(shù)來異步生成和使用值。這意味著,例如,F(xiàn)low 可以安全地發(fā)出網(wǎng)絡請求以生成下一個值,而不會阻塞主線程。
數(shù)據(jù)流涉及三個實體:
- 提供方會生成添加到數(shù)據(jù)流中的數(shù)據(jù)。得益于協(xié)程,數(shù)據(jù)流還可以異步生成數(shù)據(jù)。
- (可選)中介可以修改發(fā)送到數(shù)據(jù)流的值,或修正數(shù)據(jù)流本身。
- 使用方則使用數(shù)據(jù)流中的值。
在 Android 中,代碼庫通常是界面數(shù)據(jù)的提供方,其將界面用作最終顯示數(shù)據(jù)的使用方。 而其他時候,UI 層是用戶輸入事件的生產(chǎn)者,而層次結(jié)構(gòu)的其他層使用它們。 提供方和使用方之間的層通常充當中介,修改數(shù)據(jù)流以使其適應下一層的要求。
創(chuàng)建 Flow
要創(chuàng)建流,請使用flow
構(gòu)建器 API。 流構(gòu)建器函數(shù)創(chuàng)建一個新的 Flow,你可以在其中使用 emit 函數(shù)手動將新值發(fā)送到數(shù)據(jù)流中。
在以下示例中,數(shù)據(jù)源以固定的時間間隔自動獲取最新新聞資訊。 由于掛起函數(shù)不能返回多個連續(xù)值,數(shù)據(jù)源創(chuàng)建并返回一個數(shù)據(jù)流來滿足這個要求。 在這種情況下,數(shù)據(jù)源充當提供方。
class NewsRemoteDataSource(
private val newsApi: NewsApi,
private val refreshIntervalMs: Long = 5000
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
while(true) {
val latestNews = newsApi.fetchLatestNews()
emit(latestNews) // 將請求的結(jié)果發(fā)送到數(shù)據(jù)流
delay(refreshIntervalMs) // 暫停協(xié)程一段時間
}
}
}
// 提供一種通過掛起功能發(fā)出網(wǎng)絡請求的方法的接口
interface NewsApi {
suspend fun fetchLatestNews(): List<ArticleHeadline>
}
flow
構(gòu)建器在協(xié)程中執(zhí)行。 因此,它受益于相同的異步 API,但有一些限制:
- 數(shù)據(jù)流是有順序的。當協(xié)程內(nèi)的提供方調(diào)用掛起函數(shù)時,提供方會掛起,直到掛起函數(shù)返回。 在示例中,提供方會掛起,直到
fetchLatestNews
網(wǎng)絡請求完成為止。只有這樣,請求結(jié)果才會發(fā)送到數(shù)據(jù)流中。 - 使用
flow
構(gòu)建器,生產(chǎn)者不能從不同的 CoroutineContext 發(fā)出值。 因此,不要通過創(chuàng)建新的協(xié)程或使用 withContext 代碼塊在不同的 CoroutineContext 中調(diào)用 emit。 在這些情況下,您可以使用其他流構(gòu)建器,例如 callbackFlow。
修改數(shù)據(jù)流
中介可以使用中間運算符來修改數(shù)據(jù)流,而無需使用這些值。 這些操作符是函數(shù),當應用于數(shù)據(jù)流時,會設置一系列暫不執(zhí)行的鏈式運算,直到將來使用這些值時才會執(zhí)行這些操作。 在 Flow 參考文檔中了解有關(guān)中間運算符的更多信息。
在下面的示例中,存儲庫層使用中間運算符 map 來轉(zhuǎn)換要在 View
上顯示的數(shù)據(jù):
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource,
private val userData: UserData
) {
/**
* 返回對應流轉(zhuǎn)換的最喜歡的最新新聞資訊。這些操作是惰性的,不會觸發(fā)流程。
* 它們只是轉(zhuǎn)換流在該時間點發(fā)出的當前值。
*/
val favoriteLatestNews: Flow<List<ArticleHeadline>> =
newsRemoteDataSource.latestNews
// 過濾收藏主題列表的中間操作
.map { news -> news.filter { userData.isFavoriteTopic(it) } }
// 將最新消息保存在緩存中的中間操作
.onEach { news -> saveInCache(news) }
}
中間運算符可以接連應用,形成鏈式運算,在數(shù)據(jù)項被發(fā)送到數(shù)據(jù)流時延遲執(zhí)行。 請注意,僅將一個中間運算符應用于數(shù)據(jù)流不會啟動數(shù)據(jù)流收集。
從 Flow(數(shù)據(jù)流) 中收集
使用終端運算符可觸發(fā)數(shù)據(jù)流開始監(jiān)聽值。如需獲取數(shù)據(jù)流中的所有發(fā)出值,請使用 collect。 你可以在官方 Flow 文檔中了解更多關(guān)于終端運算符的信息。
因為 collect
是一個掛起函數(shù),所以它需要在協(xié)程中執(zhí)行。 它接受 lambda 作為在每個新值上調(diào)用的參數(shù)。 由于它是一個掛起函數(shù),調(diào)用 collect
的協(xié)程可能會掛起,直到流程關(guān)閉。
繼續(xù)前面的示例,這里是一個 ViewModel 的簡單實現(xiàn),它使用來自存儲庫層的數(shù)據(jù):
class LatestNewsViewModel(
private val newsRepository: NewsRepository
) : ViewModel() {
init {
viewModelScope.launch {
// 使用 collect 觸發(fā)流并使用其元素
newsRepository.favoriteLatestNews.collect { favoriteNews ->
// 使用最新喜歡的新聞資訊更新視圖
}
}
}
}
收集數(shù)據(jù)流會觸發(fā)提供方刷新最新消息,并以固定時間間隔發(fā)出網(wǎng)絡請求的結(jié)果。 由于提供方在 while(true) 循環(huán)中始終保持活動狀態(tài),因此當 ViewModel
被清除并 viewModelScope
被取消時,數(shù)據(jù)流將被關(guān)閉。
收集數(shù)據(jù)流可能會因以下原因停止:
- 如上例所示,協(xié)程收集被取消。此操作也會讓底層提供方停止活動。
- 提供方完成發(fā)出數(shù)據(jù)項。在這種情況下,數(shù)據(jù)流將關(guān)閉,調(diào)用
collect
的協(xié)程則繼續(xù)執(zhí)行。
除非使用其他中間運算符指定流,否則數(shù)據(jù)流始終為冷數(shù)據(jù)并延遲執(zhí)行。 這意味著每次在流上調(diào)用終端操作符時都會執(zhí)行提供方的代碼。 在前面的示例中,擁有多個流收集器會導致數(shù)據(jù)源在不同的固定時間間隔內(nèi)多次獲取最新消息。 要在多個消費者同時收集時優(yōu)化和共享數(shù)據(jù)流,請使用 shareIn 運算符。
捕捉意外的異常
提供方的數(shù)據(jù)實現(xiàn)可以來自第三方庫。 這意味著它可能會拋出意外的異常。 要處理這些異常,請使用 catch 中間運算符。
class LatestNewsViewModel(
private val newsRepository: NewsRepository
) : ViewModel() {
init {
viewModelScope.launch {
newsRepository.favoriteLatestNews
// 中介捕獲操作員。 如果拋出異常,
// 捕獲并更新 UI
.catch { exception -> notifyError(exception) }
.collect { favoriteNews ->
// 使用最新喜歡的新聞資訊更新視圖
}
}
}
}
在前面的示例中,當發(fā)生異常時,不會調(diào)用 collect
lambda,因為尚未收到新數(shù)據(jù)項。
catch
還可執(zhí)行 emit
操作,向數(shù)據(jù)流發(fā)出數(shù)據(jù)項。示例存儲庫層可以改為對緩存值執(zhí)行 emit
操作:
class NewsRepository(...) {
val favoriteLatestNews: Flow<List<ArticleHeadline>> =
newsRemoteDataSource.latestNews
.map { news -> news.filter { userData.isFavoriteTopic(it) } }
.onEach { news -> saveInCache(news) }
// 如果發(fā)生錯誤,則發(fā)出最后緩存的值
.catch { exception -> emit(lastCachedNews()) }
}
在此示例中,當發(fā)生異常時,將調(diào)用 collect
lambda,因為由于異常而將新數(shù)據(jù)項發(fā)送到數(shù)據(jù)流中。
在不同的 CoroutineContext 中執(zhí)行
默認情況下,flow
構(gòu)建器的提供方在從它收集的協(xié)程的 CoroutineContext
中執(zhí)行,并且如前所述,它不能從不同的 CoroutineContext
對值執(zhí)行 emit
操作。 在某些情況下,這種行為可能是不可取的。 例如,在本文章中使用的示例中,存儲庫層不應在 viewModelScope
使用的 Dispatchers.Main
上執(zhí)行操作。
要更改流的 CoroutineContext
,請使用中間運算符 flowOn
。 flowOn
改變了上游流的 CoroutineContext
,這意味提供方和任何在 flowOn
之前(或之上)應用的中間操作符。 下游數(shù)據(jù)流(晚于 flowOn
的中間運算符和使用方)不受影響,并會在 CoroutineContext 上執(zhí)行以從數(shù)據(jù)流執(zhí)行 collect
操作。 如果有多個 flowOn
操作符,每個操作符都會從其當前位置更改上游數(shù)據(jù)流。
class NewsRepository(
private val newsRemoteDataSource: NewsRemoteDataSource,
private val userData: UserData,
private val defaultDispatcher: CoroutineDispatcher
) {
val favoriteLatestNews: Flow<List<ArticleHeadline>> =
newsRemoteDataSource.latestNews
.map { news -> // 在默認調(diào)度程序上執(zhí)行
news.filter { userData.isFavoriteTopic(it) }
}
.onEach { news -> // 在默認調(diào)度程序上執(zhí)行
saveInCache(news)
}
// flowOn 影響上游流 ↑
.flowOn(defaultDispatcher)
// 下游流 ↓ 不受影響
.catch { exception -> // 在消費者的上下文中執(zhí)行
emit(lastCachedNews())
}
}
使用此代碼,·
onEach
和 map
運算符使用 defaultDispatcher
,而 catch
運算符和使用者在 viewModelScope
使用的 Dispatchers.Main
上執(zhí)行。
由于數(shù)據(jù)源層正在執(zhí)行 I/O 工作,因此你應該使用針對 I/O 操作進行了優(yōu)化的調(diào)度程序:
class NewsRemoteDataSource(
...,
private val ioDispatcher: CoroutineDispatcher
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
// 在 IO 調(diào)度程序上執(zhí)行
...
}
.flowOn(ioDispatcher)
}
Jetpack 庫中的數(shù)據(jù)流
Flow 已集成到許多 Jetpack 庫中,并且在 Android 第三方庫中很受歡迎。 Flow 非常適合實時數(shù)據(jù)更新和無限的數(shù)據(jù)流。
你可以將 Flow 與 Room 結(jié)合使用,以便在數(shù)據(jù)庫發(fā)生更改時收到通知。 使用數(shù)據(jù)訪問對象 (DAO) 時,返回 Flow 類型以獲取實時更新。
@Dao
abstract class ExampleDao {
@Query("SELECT * FROM Example")
abstract fun getExamples(): Flow<List<Example>>
}
每當 Example
數(shù)據(jù)表發(fā)生更改時,系統(tǒng)都會發(fā)出包含數(shù)據(jù)庫新數(shù)據(jù)項的新列表。
將 callback-based APIs 轉(zhuǎn)換為數(shù)據(jù)流
callbackFlow 是一個數(shù)據(jù)流構(gòu)建器,可讓你將 callback-based APIs轉(zhuǎn)換為 數(shù)據(jù)流。
與 flow
構(gòu)建器不同,callbackFlow
允許使用 send 函數(shù)從不同的 CoroutineContext
或使用 offer
函數(shù)在協(xié)程外部發(fā)出值。
在協(xié)程內(nèi)部,callbackFlow
使用一個 channel,它在概念上與阻塞隊列非常相似。 通道都有容量配置,限定了可緩沖元素數(shù)的上限。 callbackFlow
中創(chuàng)建的通道的默認容量為 64 個元素。 當你嘗試將新元素添加到完整頻道時,send
會將數(shù)據(jù)提供方掛起,直到有新元素的空間,而 offer
不會將相關(guān)元素添加到通道中,并會立即返回 false。
Kotlin Channel和阻塞隊列很類似,區(qū)別在于Channel用掛起的send操作代替了阻塞的put,用掛起的receive操作代替了阻塞的take。
使用lifecycle-runtime-ktx庫中的launchWhenX方法,對Channel的收集協(xié)程會在組件生命周期 < X時掛起,從而避免異常。也可以使用repeatOnLifecycle(State) 來在UI層收集,當生命周期 < State時,會取消協(xié)程,恢復時再重新啟動協(xié)程。
看起來使用Channel承載事件是個不錯的選擇,并且一般來說事件分發(fā)都是一對一,因此并不需要支持一對多的BroadcastChannel(后者已經(jīng)逐漸被廢棄,被SharedFlow替代)
如何創(chuàng)建Channel?看一下Channel對外暴露可供使用的構(gòu)造方法,考慮傳入合適的參數(shù)。
public fun <E> Channel(
// 緩沖區(qū)容量,當超出容量時會觸發(fā)onBufferOverflow指定的策略
capacity: Int = RENDEZVOUS,
// 緩沖區(qū)溢出策略,默認為掛起,還有DROP_OLDEST和DROP_LATEST
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
// 處理元素未能成功送達處理的情況,如訂閱者被取消或者拋異常
onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E>
首先Channel是熱的,即任意時刻發(fā)送元素到Channel即使沒有訂閱者也會執(zhí)行。所以考慮到存在訂閱者協(xié)程被取消時發(fā)送事件的情況,即存在Channel處在無訂閱者時的空檔期收到事件情況。例如當Activity使用repeatOnLifecycle方法啟動協(xié)程去消費ViewModel持有的Channel里的事件消息,當前Activity因為處于STOPED狀態(tài)而取消了協(xié)程。
StateFlow(狀態(tài)流) 和 SharedFlow(共享流)
StateFlow
和 SharedFlow
是 Flow API,允許數(shù)據(jù)流以最優(yōu)方式發(fā)出狀態(tài)更新并向多個使用方發(fā)出值。
StateFlow和SharedFlow,兩者擁有Channel的很多特性,可以看作是將Flow推向臺前,將Channel雪藏幕后的一手重要操作。
首先二者都是熱流,并支持在構(gòu)造器外發(fā)射數(shù)據(jù)。簡單看下它們的構(gòu)造方法
public fun <T> MutableSharedFlow(
// 每個新的訂閱者訂閱時收到的回放的數(shù)目,默認0
replay: Int = 0,
// 除了replay數(shù)目之外,緩存的容量,默認0
extraBufferCapacity: Int = 0,
// 緩存區(qū)溢出時的策略,默認為掛起。只有當至少有一個訂閱者時,onBufferOverflow才會生效。當無訂閱者時,只有最近replay數(shù)目的值會保存,并且onBufferOverflow無效。
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
)
//MutableStateFlow等價于使用如下構(gòu)造參數(shù)的SharedFlow
MutableSharedFlow(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
StateFlow
StateFlow
是一個狀態(tài)容器式可觀察數(shù)據(jù)流,可以向其收集器發(fā)出當前狀態(tài)更新和新狀態(tài)更新。還可通過其 value
屬性讀取當前狀態(tài)值。如需更新狀態(tài)并將其發(fā)送到數(shù)據(jù)流,請為 MutableStateFlow
類的 value
屬性分配一個新值。
在 Android 中,StateFlow
非常適合需要讓可變狀態(tài)保持可觀察的類。
按照 Kotlin 數(shù)據(jù)流中的示例,可以從 LatestNewsViewModel
公開 StateFlow
,以便 View
能夠監(jiān)聽界面狀態(tài)更新,并自行使屏幕狀態(tài)在配置更改后繼續(xù)有效。
class LatestNewsViewModel(
private val newsRepository: NewsRepository
) : ViewModel() {
// Backing property to avoid state updates from other classes
private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
// The UI collects from this StateFlow to get its state updates
val uiState: StateFlow<LatestNewsUiState> = _uiState
init {
viewModelScope.launch {
newsRepository.favoriteLatestNews
// Update View with the latest favorite news
// Writes to the value property of MutableStateFlow,
// adding a new element to the flow and updating all
// of its collectors
.collect { favoriteNews ->
_uiState.value = LatestNewsUiState.Success(favoriteNews)
}
}
}
}
// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
data class Success(news: List<ArticleHeadline>): LatestNewsUiState()
data class Error(exception: Throwable): LatestNewsUiState()
}
負責更新 MutableStateFlow
的類是提供方,從 StateFlow
收集的所有類都是使用方。與使用 flow
構(gòu)建器構(gòu)建的冷數(shù)據(jù)流不同,StateFlow
是熱數(shù)據(jù)流:從此類數(shù)據(jù)流收集數(shù)據(jù)不會觸發(fā)任何提供方代碼。StateFlow
始終處于活躍狀態(tài)并存于內(nèi)存中,而且只有在垃圾回收根中未涉及對它的其他引用時,它才符合垃圾回收條件。
當新使用方開始從數(shù)據(jù)流中收集數(shù)據(jù)時,它將接收信息流中的最近一個狀態(tài)及任何后續(xù)狀態(tài)。您可在 LiveData
等其他可觀察類中找到此操作行為。
與處理任何其他數(shù)據(jù)流一樣,View
會監(jiān)聽 StateFlow
:
class LatestNewsActivity : AppCompatActivity() {
private val latestNewsViewModel = // getViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
...
// 在生命周期范圍內(nèi)啟動協(xié)程
lifecycleScope.launch {
// 每次 ifecycle 處于 STARTED 狀態(tài)(或更高)時,repeatOnLifecycle 在新的協(xié)程中啟動塊,
// 并在它停止時取消它。
repeatOnLifecycle(Lifecycle.State.STARTED) {
// 觸發(fā)流程并開始監(jiān)聽值。
// 請注意,當生命周期開始時會發(fā)生這種情況,當生命周期停止時會停止收集
latestNewsViewModel.uiState.collect { uiState ->
// 收到的新值
when (uiState) {
is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
is LatestNewsUiState.Error -> showError(uiState.exception)
}
}
}
}
}
}
警告:如果需要更新界面,切勿使用 launch
或 launchIn
擴展函數(shù)從界面直接收集數(shù)據(jù)流。即使 View 不可見,這些函數(shù)也會處理事件。此行為可能會導致應用崩潰。 為避免這種情況,請使用 repeatOnLifecycle
API(如上所示)。
注意:repeatOnLifecycle
API 僅在 androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01
庫及更高版本中提供。
如需將任何數(shù)據(jù)流轉(zhuǎn)換為 StateFlow
,請使用 stateIn
中間運算符。
StateFlow、Flow 和 LiveData
StateFlow
和 LiveData
具有相似之處。兩者都是可觀察的數(shù)據(jù)容器類,并且在應用架構(gòu)中使用時,兩者都遵循相似模式。
但請注意,StateFlow
和 LiveData
的行為確實有所不同:
-
StateFlow
需要將初始狀態(tài)傳遞給構(gòu)造函數(shù),而LiveData
不需要。 - 當 View 進入
STOPPED
狀態(tài)時,LiveData.observe()
會自動取消注冊使用方,而從StateFlow
或任何其他數(shù)據(jù)流收集數(shù)據(jù)的操作并不會自動停止。如需實現(xiàn)相同的行為,您需要從Lifecycle.repeatOnLifecycle
塊收集數(shù)據(jù)流。
利用 shareIn
使冷數(shù)據(jù)流變?yōu)闊釘?shù)據(jù)流
StateFlow
是熱數(shù)據(jù)流,只要該數(shù)據(jù)流被收集,或?qū)λ娜魏纹渌迷诶厥崭写嬖冢摂?shù)據(jù)流就會一直存于內(nèi)存中。您可以使用 shareIn
運算符將冷數(shù)據(jù)流變?yōu)闊釘?shù)據(jù)流。
以在 Kotlin 數(shù)據(jù)流中創(chuàng)建的 callbackFlow
為例,您無需為每個收集器都創(chuàng)建一個新數(shù)據(jù)流,而是可以使用 shareIn
在收集器間共享從 Firestore 檢索到的數(shù)據(jù)。您需要傳入以下內(nèi)容:
- 用于共享數(shù)據(jù)流的
CoroutineScope
。此作用域函數(shù)的生命周期應長于任何使用方,以使共享數(shù)據(jù)流在足夠長的時間內(nèi)保持活躍狀態(tài)。 - 要重放 (replay) 至每個新收集器的數(shù)據(jù)項數(shù)量。
- “啟動”行為政策。
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
在此示例中,latestNews
數(shù)據(jù)流將上次發(fā)出的數(shù)據(jù)項重放至新收集器,只要 externalScope
處于活躍狀態(tài)并且存在活躍收集器,它就會一直處于活躍狀態(tài)。當存在活躍訂閱者時,SharingStarted.WhileSubscribed()
“啟動”政策將使上游提供方保持活躍狀態(tài)。可使用其他啟動政策,例如使用 SharingStarted.Eagerly
可立即啟動提供方,使用 SharingStarted.Lazily
可在第一個訂閱者出現(xiàn)后開始共享數(shù)據(jù),并使數(shù)據(jù)流永遠保持活躍狀態(tài)。
注意:如需詳細了解 externalScope
的模式,請查看這篇文章。
SharedFlow
shareIn
函數(shù)會返回一個熱數(shù)據(jù)流 SharedFlow
,此數(shù)據(jù)流會向從其中收集值的所有使用方發(fā)出數(shù)據(jù)。SharedFlow
是 StateFlow
的可配置性極高的泛化數(shù)據(jù)流。
您無需使用 shareIn
即可創(chuàng)建 SharedFlow
。例如,您可以使用 SharedFlow
將 tick 信息發(fā)送到應用的其余部分,以便讓所有內(nèi)容定期同時刷新。除了獲取最新資訊之外,您可能還想要使用用戶最喜歡的主題集刷新用戶信息部分。在以下代碼段中,TickHandler
公開了 SharedFlow
,以便其他類知道要在何時刷新其內(nèi)容。與 StateFlow
一樣,請在類中使用類型 MutableSharedFlow
的后備屬性將數(shù)據(jù)項發(fā)送給數(shù)據(jù)流:
// 當應用程序的內(nèi)容需要刷新時集中的類
class TickHandler(
private val externalScope: CoroutineScope,
private val tickIntervalMs: Long = 5000
) {
// Backing property to avoid flow emissions from other classes
private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
val tickFlow: SharedFlow<Event<String>> = _tickFlow
init {
externalScope.launch {
while(true) {
_tickFlow.emit(Unit)
delay(tickIntervalMs)
}
}
}
}
class NewsRepository(
...,
private val tickHandler: TickHandler,
private val externalScope: CoroutineScope
) {
init {
externalScope.launch {
// Listen for tick updates
tickHandler.tickFlow.collect {
refreshLatestNews()
}
}
}
suspend fun refreshLatestNews() { ... }
...
}
您可通過以下方式自定義 SharedFlow
行為:
通過 replay
,您可以針對新訂閱者重新發(fā)送多個之前已發(fā)出的值。
通過 onBufferOverflow
,您可以指定相關(guān)政策來處理緩沖區(qū)中已存滿要發(fā)送的數(shù)據(jù)項的情況。默認值為 BufferOverflow.SUSPEND
,這會使調(diào)用方掛起。其他選項包括 DROP_LATEST
或 DROP_OLDEST
。
MutableSharedFlow
還具有 subscriptionCount
屬性,其中包含處于活躍狀態(tài)的收集器的數(shù)量,以便您相應地優(yōu)化業(yè)務邏輯。MutableSharedFlow
還包含一個 resetReplayCache
函數(shù),供您在不想重放已向數(shù)據(jù)流發(fā)送的最新信息的情況下使用。
SharedFlow
在無訂閱者時會丟棄數(shù)據(jù)。SharedFlow
類似BroadcastChannel, 支持被多個訂閱者訂閱,可以使同一個事件會被多次消費。
相關(guān)官方文檔:
https://developer.android.com/kotlin/flow
https://kotlinlang.org/docs/flow.html