Kotlin Flow常見場景下的使用實例
Kotlin Flow在開發(fā)中的常用場景使用
大家了解了 Flow 的創(chuàng)建與接收流程,了解 SharedFlow 創(chuàng)建的幾種方式,各個參數的用途,了解了SharedFlow的 "青春版" StateFlow 的創(chuàng)建與接收,已經他們與 LiveData 的異同。
注:這里青春版打上引號,只是調侃而已,并不是說 StateFlow 比 SharedFlow 更加輕量,而是StateFlow使用更加簡單,更加的場景化而已,使用起來感覺比較青春版而已。
那么在真實的開發(fā)環(huán)境中我們是如何使用Flow的呢?這里從幾點舉例說明一下。
一、網絡請求搭載Retrofit
之前在網上看到有人提問,為什么Retrofit不能返回 Flow 這樣的對象。使用一個 FlowCallAdapterFactory 那我就可以直接使用Flow來傳遞了。
不是不行,有第三方的依賴實現了此功能,為什么官方不出,其實官方已經給出了建議。
如果我想使用flow來傳遞數據,有哪些方式返回Flow的方式呢?這里有幾種方案
1.1 LiveDataCallAdapterFactory
Retrofit 增加了 LiveDataCallAdapterFactory,我們可以使用LiveData來包裹對象
interface NewsApi {
@POST("/wanandroid")
fun fetchNewsLiveData(
@FieldMap map:Map<String,String>
):LiveData<ApiResponse<NewsBean>>
}
使用的時候
fetchNewsLiveData().asFlow()
這樣不就轉成了Flow了嗎?如果想轉為StateFlow 或者SharedFlow,可以繼續(xù)shareIn stateIn 之類的方法轉換為熱流。
1.2 suspend
使用掛起函數直接返回對象,然后使用flow函數創(chuàng)建出Flow對象,也是非常的簡單,這也是官方推薦的方式。
interface NewsApi {
@POST("/wanandroid")
suspend fun fetchNews(
@FieldMap map:Map<String,String>
):ApiResponse<NewsBean>
}
使用的時候,直接就創(chuàng)建了一個flow對象
flow {
emit(fetchNews())
}
如果想轉為StateFlow 或者SharedFlow,可以繼續(xù)shareIn stateIn 之類的方法轉換為熱流。
這種網絡數據使用Flow的方式,好處是可以很方便的進行合并,合流,展平等操作,很方便的使用操作符轉換成我們想要的數據。
二、協程與Flow的選擇與差異
協程與Flow的選擇,什么情況下我應該使用協程請求網絡,什么情況下我才使用Flow 來操作UI。
其實我們對于實時性不高的數據,我們可以使用 Kotlin 協程處理,而對于實時性較高的數據,我們可以用 Flow 來處理。

例如動態(tài)詳情頂部是詳情數據固定的數據,而底部是列表和點贊評論的數量,這些是動態(tài)的數據,那么我們再頂部就可以用協程請求,在底部我們使用Flow處理數據再通知其改變。
@POST("/wanandroid")
suspend fun fetchNews(
@FieldMap map: Map<String, String>
): BaseBean<NewsBean>
suspend fun fetchNewsDetail(): OkResult<NewsBean> {
return extRequestHttp {
DemoRetrofit.apiService.fetchNews(
mapOf("id" to "12232", "key" to "2")
)
}
}
lifecycleScope.launch {
val detail = mViewModel.mRepository.fetchNewsDetail()
detail.checkSuccess {
updateUI(it)
}
}
private fun updateUI(newsBean: NewsBean?) {
// XXX
}
而下面的列表與動態(tài)點贊分享數據,我們可以使用 Flow 來操作,當點贊或轉發(fā)數發(fā)生變化時,updateUI() 會被執(zhí)行,UI根據最新的數據更新。
private val _stateFlow = MutableStateFlow("")
val stateFlow: StateFlow<String> = _searchFlow
fun changeState() {
viewModelScope.launch {
val detail = mRepository.changeState()
detail.checkSuccess {
//進一系列的數據合流
//進行一系列的排序、轉換之后設置給Flow
_stateFlow.value = it ?: ""
}
}
}
操作UI的偽代碼如下:
private fun changeData() {
mViewModel.changeState()
}
private fun updateUI() {
//更新一些UI
}
override fun startObserve() {
lifecycleScope.launchWhenCreated {
mViewModel.stateFlow.collect {
updateUI()
}
}
}
是不是靜態(tài)的頁面不能用 Flow ,能不能用 LiveData ? 當然可以用了,上面的只是推薦使用,其他的方式當然都可以例如:
fun getNewsDetail(): LiveData<NewsBean?> {
return liveData {
val detail = mRepository.fetchNewsDetail()
if (detail is OkResult.Success) {
emit(detail.data)
} else {
emit(null)
}
}
}
使用的時候:
fun getData(){
mViewModel.getNewsDetail().observe(this) {
updateUI()
}
}
private fun updateUI() {
//更新一些UI
}
三、StateFlow與SharedFlow的選擇
什么時候使用StateFlow ,什么時候使用 SharedFlow ,在之前 SharedFlow 的文章中,我們對比過 StateFlow,SharedFlow,LiveData 的區(qū)別。
關于 SharedFlow、StateFlow、LiveData的對比,個人的結論是:根據不同的場景 LiveData StateFlow SharedFlow 都有自己特定的使用場景,誰也無法真的完全平替誰。誰也不是誰的超集,都是各有利弊,按需選擇即可。這里不過多贅述。
那其實從另一角度,我們區(qū)別不同的場景為狀態(tài)和事件,看此場景是狀態(tài)驅動還是事件驅動的。
比如我現在點擊了按鈕,需要彈窗了,然后使用StateFlow來記錄狀態(tài),然后收集到這個事件彈出彈框了,然后我們關閉彈窗去瀏覽此頁面的其他信息了了,但是當我們旋轉手機屏幕之后,我們會發(fā)現彈窗又出來了。這就不合理了。
有同學說,這是StateFlow的問題,此情況我們需要使用LiveData,那LiveData就沒有問題了嗎?
我們測試一下:
@HiltViewModel
class Demo4ViewModel @Inject constructor(
val mRepository: Demo5Repository,
val savedState: SavedStateHandle
) : BaseViewModel() {
val channel = Channel<String>(Channel.CONFLATED)
private val _searchLD = MutableLiveData<String>()
val searchLD: LiveData<String> = _searchLD
private val _searchFlow = MutableStateFlow("")
val searchFlow: StateFlow<String> = _searchFlow
private val _sharedFlow = MutableSharedFlow<String>(replay = 1, onBufferOverflow = BufferOverflow.SUSPEND)
val sharedFlow: SharedFlow<String> = _sharedFlow
fun changeSearch(keyword: String) {
_sharedFlow.tryEmit(keyword)
_searchFlow.value = keyword
_searchLD.value = keyword
channel.trySend(keyword)
}
}
我們測試 LiveData Channel StateFlow SharedFlow(replay=1)
點擊按鈕發(fā)送事件

旋轉屏幕查看Log-3個數據

除了Channel,原來你們都會再次觸發(fā),別急我們修改SharedFlow(replay =0)

旋轉屏幕查看Log-2個數據

SharedFlow就不會再觸發(fā)了。
到這里,StateFlow 與 SharedFlow 的使用場景就應該很清晰了,狀態(tài)(State)用 StateFlow ;事件(Event)用 SharedFlow
關于SateFlow SharedFlow LiveData 的對比可以看這里。
總結
Flow 的使用總的來說還是很廣泛,如果你的項目是Kotlin語言開發(fā)的,強烈建議使用Flow。
關于LiveData 替換為Flow的問題,這幾篇文章也給出了答案,看不同的場景,SateFlow SharedFlow LiveData 各有優(yōu)缺點,無法真的說誰能真的完全平替誰。
以上就是Kotlin Flow常見場景下的使用實例的詳細內容,更多關于Kotlin Flow使用場景的資料請關注腳本之家其它相關文章!
相關文章
詳解Android 利用Iptables實現網絡黑白名單(防火墻)
這篇文章主要介紹了詳解Android 利用Iptables實現網絡黑白名單(防火墻),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
Android入門之TabHost與TabWidget實例解析
這篇文章主要介紹了Android入門之TabHost與TabWidget,對于Android初學者有一定的學習借鑒價值,需要的朋友可以參考下2014-08-08
Android中Java instanceof關鍵字全面解析
instanceof關鍵字用于判斷一個引用類型變量所指向的對象是否是一個類(或接口、抽象類、父類)的實例.這篇文章主要介紹了Android中Java instanceof關鍵字全面解析的相關資料,需要的朋友可以參考下2016-07-07
Android使用WebView.loadUri()打開網頁的方法
這篇文章主要介紹了Android使用WebView.loadUri()打開網頁的方法,結合實例形式分析了Android中WebView控件的loadUri()打開網頁的使用技巧,需要的朋友可以參考下2016-01-01

