download: 马 SB-Python 全栈工程师 图解 Python 语法 -2022 最新完结
Jetpack 架构演变(一):初步使用 flow,附加经典案例
在 jetpack 体系中 livedata 的角色纯纯粹粹是个桥接器,DataSource 中获取到数据,而后由 viewmodel 进行逻辑处理,最初被 livedata.postValue 到 view 层,唯一的价值是绑定了 lifecycle,只在页面活跃(start)的时候接受数据
官网的一篇介绍可能参考:从 LiveData 迁徙到 Kotlin 数据流 – 掘金
对于初学者来说使用 lieveData 的好处是足够简略和绝对安全
引入 flow 次要因为以下几点:
具备更敌对的 API,学习成本较低
跟 Kotlin 协程、LiveData 拆散更紧密,Flow 能够转换成 LiveData,在 ViewModel 中间接使用
拆散协程的作用域,当协程被勾销时,Flow 也会被勾销,避免内存泄漏
flow 库从属于 kotlin,livedata 属于 Android,托付 Android 平台的限度对于未来跨平台发展无利
【flow 是个冷数据流】
所谓冷流,即上游无消费行为时,上游不会产生数据,只有上游开始生产,上游才开始产生数据。
而所谓热流,即无论上游是否有消费行为,上游都会自己产生数据。
下边通过一个经典场景粗疏描述下 flow(单纯的 flow,而 stateFlow 会在后续章节中讲解)的使用
案例:一个菜谱利用 app 中,我想在一个页面展示一个列表(recyclerview),此列表的每个 item 是个子列表,子列表顺次为
计划菜谱列表;
收藏菜谱列表;
根据食材筛选的菜谱列表;
根据食材获取用户偏好的菜谱列表;
四个子列表需要四个接口来获取,组装好起初刷新最初的列表
其中每个列表都有可能是空,是 emptylist 的话这行就不浮现了,因为四个接口数据量大小不同,所以不会同一时间返回,同时又要保障这四个子列表按申请的次序来展示。
思路:
设计数据结构,最外层的 data:
data class ContainerData(val title : String , val list: List)
复制代码
其中 Recipe 实体是每个菜谱
data class Recipe(val id: String,
val name: String,
val cover: String,
val type: Int,
val ingredients: List? = mutableListOf(),
val minutes: Int,
val pantryItemCount : Int )
复制代码
模拟四个请求为:
val plannlist = Request.getPlannlist()
val favouritelist= Request.getFavouritelist()
… 以此类推
如果按照申请四个请求返回秩序不同,同时申请在列表中按次序浮现,如果实现?
打算一:可能等待四个请求都返回后而后组装数据,刷新列表
可能利用协程的 await 方法:
val dataList = MutableLiveData<List>()
viewModelScope.launch {
// planner
val plannerDefer = async {Request.getPlannlist() }
// favourite
val favouriteDefer = async {Request.getFavouritelist() }
val plannerData = plannerDefer.await()
val favouriteData = favouriteDefer.await()
…. 省略后两个接口
val list = listof(
Container(“planner” , plannerData),
Container("favourite" , favouriteData),
…
)
dataList.postValue(list)
}
复制代码
await() 方法是挂起协程,四个接口异步请求(非次序),等最初一个数据请求返回后才会执行下边的步骤
而后组装数据利用 liveData 发送,在 view 中渲染
viewModel.dataList.observe(viewLifecycleOwner) {
mAdapter.submitList(it)
}
复制代码
此种形式简略,并且无效解决了按次序排列四个列表的需要,缺点是体验差,假如有一个接口极慢,其余几个就会等待它,用户看着 loading 一直发呆么。
打算二:接口间不再互相等待,哪个接口先回来就渲染哪个,问题就是如何保障次序?
有的同学会有打算:先定制一个空数据 list
val list = listOf(
Container(“planner”, emptylist()),
Container(“favourite”, emptylist()),
…
)
复制代码
而后先用 adapter 去渲染 list,哪个接口回来就去之前的列表查找替换,而后 adapter 刷新对应的数据,当然可能,不过会产生一部分逻辑胶水代码,查找遍历的操作。
此时咱们可能借助 flow 来实现了
1 构造一个 planner 数据流
val plannerFlow = flow {
val plannList = Request.getPlanlist()
emit(ContainerData(“Planner”, plannList))
}.onStart {emit(ContainerData("", emptylist()))
}
复制代码
注意是个 val 变量,不要写成 fun plannerFlow() 方法,不然每次调用开辟新栈的时候新建个 flow,并且会一直保存在内存中,直到协程勾销
其中 onStart 会在发送正式数据之前发送,作为预加载。
而后咱们就可能构造正式请求了
viewModelScope.launch {
combine(plannerFlow , favouriteFlow , xxxFlow ,xxxFlow) { planner , favourites , xxx , xxx ->
mutableListOf(planner , favourites , xxx,xxx)
}.collect {datalist.postValue(it)
}
}
复制代码
combine 的官网正文为
Returns a Flow whose values are generated with transform function by combining the most recently emitted values by each flow.
复制代码
combine 操作符可能连接两个不同的 Flow,一旦产生数据就会触发组合后的 flow 的流动,同时它是有序的。