引言:所有成功者的背后,都有一份艱苦的歷程,不要只看到了人前的風光,而低估了他們背后所付出的努力。
隨著flow到流行度越來越高,有開發者呼吁我使用flow,于是我就如你們所愿,新增了StateFlow作為新的數據載體。當然你仍然可以使用舊版本的LiveData,代碼寫法略微不同罷了。如果對我的dcache框架設計不是很理解的小伙伴,可以看我的專欄其他文章。
為什么推薦使用StateFlow
如果你非要問我為什么要使用StateFlow?我可以告訴你,因為可以裝逼,哈哈,開個玩笑。新技術的流行必然有一部分炒作的部分,但也肯定是有其改進的地方的。要講StateFlow,就不得不從flow開始說起。flow是屬于kotlin語言范疇的,你可以把它當成kotlin協程的一個API。沒錯,kotlin語言的野心就是要做跨平臺的語言,答案就在這里,LiveData是android的API,而SharedFlow與StateFlow直接就是Kotlin編程語言級別的,代碼復用性更好。
LiveData和StateFlow使用對比
以列表數據模式的Repository為例。從2.1.5開始@Repository注解拆分成了@Repository注解和@ListRepository,所以2.1.4版本你應該使用@Repository注解,而如果說,你使用的是2.1.5及以上版本的dcache庫,要使用@ListRepository注解。由于StateFlow在2.2.0版本才開始支持,所以自然要使用@ListRepository注解。
先看StateFlow的寫法。
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 此處省略代碼若干行lifecycleScope.launchWhenCreated {repository.getListFlowData().collect {adapter.setTemperatures(it)}}
}
不要忘了使用協程作用域。
然后我們調用fetchListData()。
repository.fetchListData(listener = object : OnLoadStateListener {override fun onLoad(state: Int) {Log.d("WeatherActivity", "數據是否加載成功:${state==0}")}
}, description = null)
加載狀態監聽接口和描述信息可以傳null。這個抓取數據方法一經調用,collect代碼塊就會刷新數據。由于fetchListData()天然就返回的StateFlow,所以你并不一定要分為兩步觀察數據。而如果你要分為兩步,則調用getListFlowData()或getFlowData()。
再看原來LiveData的寫法,這次我們不用list模式的Repository,如果要使用,直接配置@Repository注解。
minutelyRepository.latlng = "116.407526,39.90403"
minutelyRepository.fetchData("按分鐘統計天氣").observe(this, Observer {it?.apply {tvCacheMinutely.text = "minutely:${toString()}\n"}
})
很明顯,fetchData()返回LiveData,直接調用observe()進行數據的觀察。簡單總結下,API的設計在調用層面具有相似性,所以,無論你使用的是LiveData為數據載體的Repository還是StateFlow的,都是調用fetchData()或fetchListData()更新緩存數據,框架內部自動幫你緩存到數據庫,同時常駐在內存并遞送給UI層刷新界面。所以你可以專心開發你的業務邏輯,這是不是很棒?
package com.example.dcache.repositoryimport android.content.Context
import com.example.dcache.biz.weather.WeatherService
import com.example.dcache.model.WeatherModel
import dora.cache.data.fetcher.OnLoadStateListener
import dora.cache.repository.DoraDatabaseCacheRepository
import dora.cache.repository.Repository
import dora.http.DoraCallback
import dora.http.retrofit.RetrofitManager@Repository
class WeatherRepository(context: Context) : DoraDatabaseCacheRepository<WeatherModel>(context) {var latlng: String = ""override fun onLoadFromNetwork(callback: DoraCallback<WeatherModel>,listener: OnLoadStateListener?) {RetrofitManager.getService(WeatherService::class.java).getWeather(latlng).enqueue(callback)}
}
最后簡單復習一下Repository的寫法。詳細Demo代碼https://github.com/dora4/DoraCacheSample 。
框架設計的變化
這是StateFlow的。
/*** 用于網絡數據抓取。*/
interface IFlowDataFetcher<M> {/*** 清空flow data的數據。*/fun clearData()/*** 抓取數據的回調。*/fun callback(): DoraCallback<M>/*** 開始抓取數據。*/fun fetchData(description: String?, listener: OnLoadStateListener? = OnLoadStateListenerImpl()): StateFlow<M?>/*** 獲取flow data。*/fun getFlowData(): StateFlow<M?>
}
這是LiveData的。
package dora.cache.data.fetcherimport androidx.lifecycle.LiveData
import dora.http.DoraCallback/*** 用于網絡數據抓取。*/
interface IDataFetcher<M> {/*** 清空livedata的數據。*/fun clearData()/*** 抓取數據的回調。*/fun callback(): DoraCallback<M>/*** 開始抓取數據。*/fun fetchData(description: String?, listener: OnLoadStateListener? = OnLoadStateListenerImpl()): LiveData<M?>/*** 獲取livedata。*/fun getLiveData(): LiveData<M?>
}
是不是沒啥變化?對的,這就是架構設計的魅力所在。前期架構設計比較到位,所以只需要遵循開閉原則。對擴展開放,對修改關閉。
原先繼承BaseRepository的,現在繼承BaseFlowRepository的。名字有帶Flow單詞的,就是StateFlow的。
開源框架支持
筆者寫框架和文檔不容易,希望你的支持。你的支持是我改進優化最大的動力!
數據緩存dcache框架 https://github.com/dora4/dcache-android
dora框架的開發插件 https://github.com/dora4/dora-studio-plugin
dora框架 https://github.com/dora4/dora