download:小程序音乐项目开发实战-coderwhy
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()
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的活动,同时它是有序的。