深入探討kotlin StateFlow的兩個問題和使用場景
背景說明:
我們日常開發(fā)中,經(jīng)常要在一個獨立的界面上做網(wǎng)絡請求顯示或者toast報錯,以及錯誤信息展示。
LiveData是粘性事件,如果有值(或者有初始值),再注冊監(jiān)聽,就會立刻觸發(fā)。然后就是網(wǎng)絡請求,將結果設置到LiveData上,等待回調。
這個流程相信是99%的開發(fā)任務。其實使用Flow我認為是殺雞用牛刀。LiveData我認為在這種場景下,是更好的選擇。因為我們99%的場景并非“流”!都是一次請求,或者下拉刷新獲取一次結果并展示,而且會屏蔽中間的快速刷新動作,避免過多請求。
好了,既然談到Flow,自然要用它來替代LiveData。
開發(fā)實踐模板規(guī)范
如下是參考開發(fā)規(guī)范做的:
首先定義一個狀態(tài)包裹類,便于后續(xù)解析和分類:
sealed class StatusState<out T> {
object Loading : StatusState<Nothing>()
//官方就是data class。注意有坑,后續(xù)介紹。我推薦移除data
data class Success<out T>(val data: T) : StatusState<T>()
//官方就是data class。注意有坑,后續(xù)介紹。我推薦移除data
data class Error(val message: String?) : StatusState<Nothing>()
}
第2步,在ViewModel申明;然后調用Api接口,賦值給value:
//申明為StateFlow
private val _userInfo = MutableStateFlow<StatusState<Bean>>(StatusState.Loading)
val userInfo: StateFlow<StatusState<Bean>> = _userInfo.asStateFlow()
fun requestXXX(xxx) {
viewModelScope.launch {
try {
val data = Api.request(xx)
_userInfo.value = StatusState.Success(data)
} catch (e: Exception) {
_userInfo.value = StatusState.Error(e.message)
}
}
}第3步,在Fragment/Activity中collect,如下的 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) {}}套路也是官方推薦的:
onCreate() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userInfo.collect{
//解析為正確
//...ShowDialog()
// 解析為error
//...toast(exMsg)
}
}
}
}坑來了
1. 每次onStart就觸發(fā)一次
比如跳轉到下一頁返回,或者Home鍵再返回。立刻就觸發(fā)。像我這里一回來toast一下,不太合適吧?
為什么會這樣:
- 理解
collect{}函數(shù):- 研究了函數(shù)代碼,可以理解為一個死循環(huán),等待協(xié)程喚醒(誰?當然是我們flow發(fā)生數(shù)據(jù)變化);喚醒一次執(zhí)行一次你的collect的代碼。然后,進入下一次等待。
- 理解repeatOnLifecycle(Lifecycle.State.STARTED):
- 當生命周期離開STARTED即onStop的時候,把collect這個執(zhí)行死循環(huán)給cancel掉了。然后,當STARTED即onStart的時候,立刻觸發(fā)
collect{}動作,死循環(huán)又回來了。
- 當生命周期離開STARTED即onStop的時候,把collect這個執(zhí)行死循環(huán)給cancel掉了。然后,當STARTED即onStart的時候,立刻觸發(fā)
這就是repeatOnLifecycle(Life...START) + flow collect的原理。
- 再看StateFlow的總結特性,“熱流”屬性,始終持有最新狀態(tài)值(通過 value 屬性)。
- 新訂閱者立即獲取最新值:當收集重新啟動時(進入 STARTED),StateFlow 會立即將當前最新值(value)發(fā)送給收集器,即使該值之前已發(fā)送過。
這就導致了它的觸發(fā)。但往往我們不需要這種特性。
2. 數(shù)據(jù)相同不觸發(fā)
總結圖中有說了,自動去重,連續(xù)相同值不會觸發(fā)更新。雖然你的網(wǎng)絡請求已經(jīng)執(zhí)行,用戶手都點麻了,但是就是不觸發(fā)collect。按道理來講,相同的網(wǎng)絡結果,也就你點了幾次,就彈幾次,不然瘋狂刷新沒個反饋,不好吧?
而且明明我每次都是新建的對象呀,為什么也不更新?
//網(wǎng)絡請求結果設置 _userInfo.value = StatusState.Error(it.msg)
這又是另外一個坑,原因在于,data class會自行實現(xiàn)equals各個字段比較,相同就不觸發(fā)collect。因此當我改成不使用data class,那么他每次比較的是2個對象的地址,自然是不一樣的。這與LiveData 或者 SharedFlow是有區(qū)別的,liveData允許使用重復的對象。
怎么解決
使用SharedFlow。
首先它沒有初始值。并且,它不會相同對象或者equals相同就不更新,就可以使用data class。
而且每次重新collect的時候,并無緩存(你不修改MutableSharedFlow申明參數(shù)),也就不會觸發(fā)。
我想SharedFlow才是替代LiveData的真正最佳對象。
總結

回歸我前面說的,我們真的需要使用StateFlow嗎?
我想我們在99%普通頁面上的需求的時候,應該優(yōu)先需要的是SharedFlow。
到此這篇關于kotlin StateFlow的兩個問題和使用場景探討的文章就介紹到這了,更多相關kotlin StateFlow使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android Dialog仿ios9中UIAlertController控件
這篇文章主要為大家詳細介紹了Android Dialog仿ios9中UIAlertController控件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
Android SharedPreferences存儲用法詳解
這篇文章主要為大家詳細介紹了Android SharedPreferences存儲用法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02
詳解Android開發(fā)中Activity的四種launchMode
這篇文章主要介紹了Android開發(fā)中Activity的四種launchMode,launchMode主要用于控制多個Activity間的跳轉,需要的朋友可以參考下2016-03-03

