引言:所有成功者的背后,都有一份艰苦的历程,不要只看到了人前的风光,而低估了他们背后所付出的努力。
随着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.repository
import 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.fetcher
import 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