前面我們講到如何應coroutine的flow和liveData合作,但android其實還推出了另一個東西,stateFlow,剛好之前的line截圖,也點出了這個主題
為什麼說stateFlow是衝著liveData來的呢?
其實有去看文檔的範例就會知道,用法跟liveData超級像,連那個封裝方法都如出一轍,讓你覺得是不是想換個包裝騙我
只看文檔範例的確很像,但~還是要試過才會知道的吧
//viewModel
private val _postStateFlow = MutableStateFlow(Post(0,0,"",""))
val postStateFlow = _postStateFlow
init {
viewModelScope.launch {
repo.postFlow.collect {
_postStateFlow.value = it
}
}
}
//fragment
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED){
viewModel.postStateFlow.collect {
Timber.d(it.toString())
}
}
}
等等,除了用到corouitne以外,這不就liveData,而且比昨天的asLiveData寫起來還要長
沒事,我們先來簡化一下
//viewModel
var stateInFlow: StateFlow<Post> =
repo.postFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
Post(0, 0, "", "")
)
Tip for Android apps! You can use WhileSubscribed(5000) most of the time to keep the upstream flow active for 5 seconds more after the disappearance of the last collector. That avoids restarting the upstream flow in certain situations such as configuration changes. This tip is especially helpful when upstream flows are expensive to create and when these operators are used in ViewModels.
我翻一下,就是在某些case裡面,如螢幕旋轉,可能會有一小段時間沒有訂閱者,所以用 WhileSubscribed(時間)來定義沒訂閱者後幾秒才停止,尤其是在producer的操作消耗較多資源,且這些操作在viewModel執行的時候。
stateFlow必須設置初始值,所以每個訂閱者至少能收到一個值,如果不想對此作設置,可以透過將類別改成nullable,去做判定,或是在seal class新增一個空的狀態
stateFlow的updateState內部會檢查,如舊值和新值一樣,就不會更新
replay被強制設為1,這代表新的訂閱者會立刻收到最新的狀態
可以透過values能到單個最新值,而不是collect整個流
作為hotflow他不會自己停下,除非有特別設置(stateFlow繼承自shareFlow)
對的,這裡默默地多了一個東西SharingStarted
stateflow允許透過value同步訪問最新的值,shareflow則否
interface StateFlow<out T> : SharedFlow<T>
stateFlow是從sharedFlow改變出來的,stateFlow最後的發射值可背心添加的觀察者觀察到
在stateflow,不支援buffer,也不支援resetReplayCache,且必須有初始值.
當你需要對stateflow做出調整時,像是添加buffer,回朔多個值,省略初始值等等,可以用下列方式創建,範例為stateflow的基本實現,可根據需求做調整
// MutableStateFlow(initialValue) is a shared flow with the following parameters:
val shared = MutableSharedFlow(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
shared.tryEmit(initialValue) // emit the initial value
val state = shared.distinctUntilChanged() // get StateFlow-like behavior
如果應用程序在後台,我們不需要總是監聽位置更新。但是,我們需要緩存最後發出的項目,以便用戶在獲取當前位置時始終可以在屏幕上看到一些數據,即使是陳舊的數據。對於這種情況,我們可以使用stateIn運算符。
都只有唯一值
允許被多個觀察者/訂閱者 調用
永遠提供觀察者/訂閱者 最新值,與 觀察者/訂閱者 數量無關
支持databinding
必須設置初始值
方便切換thread
不要用fun去創建stateFlow,這樣會在每次呼叫viewModel.createStateFlow都創建一個新的實例
// DO NOT USE shareIn or stateIn in a function like this.
// It creates a new SharedFlow/StateFlow per invocation which is not reused!
fun createStateFlow() =
repo.postFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
Post(0, 0, "", "")
)
floe是冷數據流,而stateFlow和sharedFlow是熱數據流,他們的差別在於,flow在每次collect時,都會創建一個實例,而state和share在已經有實例的情況下,會觀察同一個實例,所以他們可以收到同一個值
白話一點就是,他們會把流的值,給多個觀察者,flow不行
Cold flows are created on-demand and emit data when they’re being observed. Hot flows are always active and can emit data regardless of whether or not they’re being observed.
distinctUntilChanged - 回傳一個流,且會過濾和前一個相同的值,主要用於提高性能,且在沒有訂閱者時,保留一個舊值
A state flow is a hot flow because its active instance exists independently of the presence of collectors. Its current value can be retrieved via the value property.
stateFlow作為熱流,因為他的實例可以獨立於collecter而存在,同時他也可以透過value,取值或設值
State flow never completes. A call to Flow.collect on a state flow never completes normally, and neither does a coroutine started by the Flow.launchIn function.
更重要的是,stateFlow永遠不會結束,flow.collect永遠不會正常的結束,所以務必要用repeatOnLifecycle來確保穩定性。
launchin也無法正常運作
在mutableStateflow vlaue的更新總是透過conflate,意即在大量操作的情況下,中間一部份的值會丟失,但總是能確保獲取最新的值
stateFlow繼承自shareFlow,特色是指存一個值,可用來推播體驗,或是身分認證