当前位置: 首页 > news >正文

建网站知乎58同城枣庄网站建设

建网站知乎,58同城枣庄网站建设,邯郸高端网站建设,石家庄做网站排名公司哪家好目录 前言Flow生命周期StateFlow 替代LiveDataSharedFlow其他常见应用场景处理复杂、耗时逻辑存在依赖关系的接口请求组合多个接口的数据 Flow使用注意事项总结 前言 前面两篇文章#xff0c;介绍了Flow是什么#xff0c;如何使用#xff0c;以及相关的操作符进阶#xff… 目录 前言Flow生命周期StateFlow 替代LiveDataSharedFlow其他常见应用场景处理复杂、耗时逻辑存在依赖关系的接口请求组合多个接口的数据 Flow使用注意事项总结 前言 前面两篇文章介绍了Flow是什么如何使用以及相关的操作符进阶接下来这篇文章主要介绍Flow在实际项目中使用。 Flow生命周期 在介绍Flow实际应用场景之前我们先回顾Flow第一篇介绍的计时器例子我们在ViewModel定义了一个timeFlow数据流 class MainViewModel : ViewModel() {val timeFlow flow {var time 0while (true) {emit(time)delay(1000)time} }然后Activity里面接收前面定义的数据流。 lifecycleOwner.lifecycleScope.launch {viewModel.timeFlow.collect { time -times timeLog.d(ddup, update UI $times)}}我运行看下实际效果 你们有没有发现,App切换到后台时日志还在打印这不是对资源的浪费我们修改一下接收的地方代码 lifecycleOwner.lifecycleScope.launchWhenStarted {viewModel.timeFlow.collect { time -times timeLog.d(ddup, update UI $times)}}我们把协程开启的方法从launch改成launchWhenStarted再运行看下效果 我们可以看到当点击HOME键退回到后台的时候日志不再打印了由此可见改动生效了但是流取消接收了吗我们切回到前台看下 切换到前台我们可以看到计数器并没有从0开始所以其实它并没有取消接收只是在后台暂停接收数据了Flow管道还保留之前的数据事实上这个launchWhenStarted API已经废弃了Google更推荐repeatOnLifecycle来代替它并且它不会存在管道中保留旧数据问题。 我们尝试改造一下对应代码 lifecycleOwner.lifecycleScope.launch {lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.timeFlow.collect { time -times timeLog.d(ddup, update UI $times)}} }重新运行看下效果 我们可以看到从后台切回到前台数据又从0开始了说明切换到后台Flow取消工作了原来的数据全部清空了。 我们在使用Flow,通过repeatOnLifecycle更能保证我们程序的安全性。 StateFlow 替代LiveData 前面介绍的都是Flow冷流例子接下来将会介绍一些热流常见的应用场景。 还是前面的计时器的例子假如横竖屏切换后又会出现什么情况呢 我们可以看到横竖屏切换后Activity重新创建重新创建后timeFlow会重新collect冷流被重新collect后重新执行然后计时器又从0开始计时了很多时候我们希望横竖屏切换时希望页面的状态是保持不变的至少在一定时间内不被改变的这里我们冷流修改成热流试下 val hotFlow timeFlow.stateIn(viewModelScope,SharingStarted.WhileSubscribed(5000),0) lifecycleOwner.lifecycleScope.launch {lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.hotFlow.collect { time -times timeLog.d(ddup, update UI $times)}} }这里着重说下stateIn里面的三个参数第一个是协程的作用域第二个是flow保持工作状态最大有效时间超过flow就会停止工作最后一个参数是初始值。 重新运行看下效果: 这里我们可以看到横竖屏切换后打印的日志计时器不会从0开始了。 我们上面介绍了一个冷流如何修改变成热流的这里还没有介绍stateFlow如何代替LiveData下面介绍一下stateFlow替代LiveData用法 private val _stateFlow MutableStateFlow(0) val stateFlow _stateFlow.asStateFlow()fun startTimer() {val timer Timer()timer.scheduleAtFixedRate(object :TimerTask() {override fun run() {_stateFlow.value 1}},0,1000) }viewModel.startTimer()lifecycleOwner.lifecycleScope.launch {lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.stateFlow.collect { time -times timeLog.d(ddup, update UI $times)}} }我们定义了一个StateFlow热流然后通过一个startTimer()方法改变stateFlow值类似LiveData setData点击按钮时开始改变StateFlow值并收集对应流的值类似LiveData Observe方法监听数据变化。 下面看下实际运行效果 到这里我们介绍完了StateFlow基本用法下面来介绍SharedFlow。 SharedFlow 要理解SharedFlow我们先知道个概念粘性事件按字面理解就是观察者订阅数据源时如果数据源已经有最新的数据那么这些数据会立即推送给观察者。从上面的解释来看LiveData是符合这个粘性特性的同样的StateFlow呢我们写个简单的demo验证一下 class MainViewModel : ViewModel() {private val _clickCountFlow MutableStateFlow(0)val clickCountFlow _clickCountFlow.asStateFlow()fun increaseClickCount() {_clickCountFlow.value 1 } } //MainActivityval tv findViewByIdTextView(R.id.tv_content) val btn findViewByIdButton(R.id.btn) btn.setOnClickListener {viewModel.increaseClickCount() }lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.clickCountFlow.collect { time -tv.text time.toString()Log.d(ddup, update UI $time)}} }我们首先在MainViewModel定义了一个clickCountFlow然后在Activity通过Button点击对clickCountFlow数据改变然后接收clickCountFlow并把数据显示在文本上。 下面看下运行效果 我们可以看到横竖屏切换的时候Activity重新创建clickCountFlow重新收集后数据还是从之前的4开始的说明StateFlow是粘性的在这里看上去没有问题但是我们看另外一个例子我们模拟一个点击登陆的场景点击登陆按钮实现登陆并登陆 //MainViewModelprivate val _loginFlow MutableStateFlow()val loginFlow _loginFlow.asStateFlow()fun startLogin() {// Handle login logic here._loginFlow.value Login Success} //MainActivity val tv findViewByIdTextView(R.id.tv_content) val btn findViewByIdButton(R.id.btn) btn.setOnClickListener {viewModel.startLogin() }lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {viewModel.loginFlow.collect {if (it.isNotBlank()) {Toast.makeText(thisMainActivity2, it, Toast.LENGTH_LONG).show()}}} }上述代码实际就是模拟一个点击登陆然后会提示登陆成功我们看下实际运行效果 看到没有横竖屏切换后登陆成功的提示重新弹出一遍我们并没有走重新登陆流程这就是粘性事件带来的数据重复接收的问题上面代码我们改成SharedFlow试下 private val _loginFlow MutableSharedFlowString()val loginFlow _loginFlow.asSharedFlow()fun startLogin() {// Handle login logic here.viewModelScope.launch {_loginFlow.emit(Login Success)}}我们StateFlow改成SharedFlow我们可以看到SharedFlow不需要初始值登陆的地方增加了emit方法发送数据接收数据的地方不变重新运行下看下效果 这里我们可以看到使用SharedFlow不会出现这个粘性问题其实SharedFlow还有很多参数可以配置的 public fun T MutableSharedFlow(// 每个新的订阅者订阅时收到的回放的数目默认0replay: Int 0,// 除了replay数目之外缓存的容量默认0extraBufferCapacity: Int 0,// 缓存区溢出时的策略默认为挂起。只有当至少有一个订阅者时onBufferOverflow才会生效。当无订阅者时只有最近replay数目的值会保存并且onBufferOverflow无效。onBufferOverflow: BufferOverflow BufferOverflow.SUSPEND)SharedFlow更多用法有待大家去发掘啊这里不过赘述了。 其他常见应用场景 前面介绍了从基本冷流到热流以及StateFlow、SharedFlow常见用法适用场景接下来我们围绕几个实际例子看看flow其他常见应用场景。 处理复杂、耗时逻辑 我们一般做一些复杂的耗时逻辑放在子线程处理然后切换到主线程展示UI同样的Flow也支持线程切换flowOn可以让之前的操作放到对应的子线程处理。 我们实现一个读取本地Assets目录下的person.json文件并将其解析出来json文件中的内容 {name: ddup,age: 101,interest: earn money... }然后解析文件 fun getAssetJsonInfo(context: Context, fileName: String): String {val strBuilder StringBuilder()var input: InputStream? nullvar inputReader: InputStreamReader? nullvar reader: BufferedReader? nulltry {input context.assets.open(fileName, AssetManager.ACCESS_BUFFER)inputReader InputStreamReader(input, StandardCharsets.UTF_8)reader BufferedReader(inputReader)var line: String?while ((reader.readLine().also { line it }) ! null) {strBuilder.append(line)}} catch (ex: Exception) {ex.printStackTrace()} finally {try {input?.close()inputReader?.close()reader?.close()} catch (e: IOException) {e.printStackTrace()}}return strBuilder.toString() }Flow读取文件 /*** 通过Flow方式获取本地文件*/ private fun getFileInfo() {lifecycleScope.launch {flow {//解析本地json文件并生成对应字符串val configStr getAssetJsonInfo(thisMainActivity2, person.json)//最后将得到的实体类发送到下游emit(configStr)}.map { json -Gson().fromJson(json, PersonModel::class.java) //通过Gson将字符串转为实体类}.flowOn(Dispatchers.IO) //在flowOn之上的所有操作都是在IO线程中进行的.onStart { Log.d(ddup, onStart) }.filterNotNull().onCompletion { Log.d(ddup, onCompletion) }.catch { ex - Log.d(ddup, catch:${ex.message}) }.collect {Log.d(ddup, collect parse result:$it)}} }最终打印日志 2024-07-09 22:00:34.006 12251-12251 ddup com.ddup.flowtest D onStart 2024-07-09 22:00:34.018 12251-12251 ddup com.ddup.flowtest D collect parse result:PersonModel(nameddup, age101, interestearn money...) 2024-07-09 22:00:34.019 12251-12251 ddup com.ddup.flowtest D onCompletion 存在依赖关系的接口请求 我们经常会遇到接口请求依赖另外一个请求的结果也就是所谓的嵌套请求嵌套过多的就会出现回调地狱我们通过FLow来实现一个类似的需求 lifecycleScope.launch {lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {//将两个flow串联起来 先搜索目的地然后到达目的地viewModel.getTokenFlows().flatMapConcat {//第二个flow依赖第一个的结果viewModel.getUserFlows(it)}.collect {tv.text it ?: error}} }组合多个接口的数据 组合多个接口的数据是一个什么样的场景呢比如说我们存在请求多个接口然后把它们的结果合并起来统一展示或者作为另外一个接口的请求参数试问一下该如何实现呢 第一种一个一个请求然后合并 第二种并发请求然后全部请求完了合并。 显然第二种效果比较高效下面看下代码 //分别请求电费、水费、网费Flow之间是并行关系 suspend fun requestElectricCost(): FlowSpendModel flow {delay(500)emit(SpendModel(电费, 10f, 500))}.flowOn(Dispatchers.IO)suspend fun requestWaterCost(): FlowSpendModel flow {delay(1000)emit(SpendModel(水费, 20f, 1000))}.flowOn(Dispatchers.IO)suspend fun requestInternetCost(): FlowSpendModel flow {delay(2000)emit(SpendModel(网费, 30f, 2000))}.flowOn(Dispatchers.IO)首先我们在ViewModel模拟定义了几个网络请求接下来合并请求 lifecycleScope.launch {val electricFlow viewModel.requestElectricCost()val waterFlow viewModel.requestWaterCost()val internetFlow viewModel.requestInternetCost()val builder StringBuilder()var totalCost 0fval startTime System.currentTimeMillis()//NOTE:注意这里可以多个zip操作符来合并Flow且多个Flow之间是并行关系electricFlow.zip(waterFlow) { electric, water -totalCost electric.cost water.costbuilder.append(${electric.info()},\n).append(${water.info()},\n)}.zip(internetFlow) { two, internet -totalCost internet.costtwo.append(internet.info()).append(,\n\n总花费$totalCost)}.collect {tv.text it.append(总耗时${System.currentTimeMillis() - startTime} ms)Log.d(ddup,${it.append(总耗时${System.currentTimeMillis() - startTime} ms)})} }运行结果 我们看到总花费时间跟最长请求的时间基本一致。 Flow使用注意事项 多个Flow不能放到一个lifecycleScope.launch里去collect{}因为进入collect{}相当于一个死循环下一行代码永远不会执行如果就想写到一个lifecycleScope.launch{}里去可以在内部再开启launch{}子协程去执行。 错误示范 lifecycleScope.launch {flow1.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect {}flow2.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect {} }正确写法 lifecycleScope.launch {launch {flow1.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect {}}launch {flow2.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect {}} }总结 我们从Flow的生命周期介绍了flow正确使用姿势避免资源的浪费到普通的冷流转换成热流再到StateFlow代替LiveData以及它的粘性问题然后通过SharedFlow解决粘性问题再到常见应用场景最后到Flow使用注意事项基本涵盖了Flow大部分特性、应用场景这也是Flow学习的最终篇。 创作不易喜欢的麻烦点赞、收藏、评论以资鼓励。 参考文章 Kotlin Flow响应式编程StateFlow和SharedFlow Kotlin | Flow数据流的几种使用场景
http://www.w-s-a.com/news/519446/

相关文章:

  • 自己创建网站教程河南省建设厅官方网站李学军
  • 一个网站需要多少容量怎样免费设计网站建设
  • 建设工程交易中心网站12306的网站是哪个公司做的
  • 建设网站经营范围自己给公司做网站
  • 河北省住房建设厅政务网站网络营销推广的岗位职责有哪些
  • 上海网站建设优化价格孝义做网站的公司
  • 哪个公司网站做的最好义乌 网站 制作
  • 百度站长工具综合查询wordpress 上传pdf
  • 旅游短租公寓网站建设深圳龙岗招聘网
  • 做海淘是在哪个网站网络查控系统设计方案
  • o2o网站建设代理商微信公众号开发文档
  • 网站设计课程总结关于网站备案的公告
  • 网站建设与运营意义到哪查找网站域名
  • 网站及单位网站建设情况眉县住房和城市建设局网站
  • 网站是否能够被恶意镜像wordpress占用
  • 经典设计网站网站等保测评怎么做
  • 重庆做网站公司贴吧廊坊公司快速建站
  • 海外贸易在什么网站做怎么排名到百度第一页
  • 线上注册公司是在哪个网站做高仿网站
  • 网站构架图网上推广平台哪个好
  • 公司网站首页图片素材vi设计的目的和意义
  • 网站的需求分析都有哪些内容济南营销型网站建设团队
  • 怎么选择优秀的网站建设公司生鲜网站开发
  • 如何编写网站建设销售的心得网站的权限管理怎么做
  • 网站业务员好做吗无忧网站优化
  • 网站随机代码网站建设费 账务处理
  • 商洛网站建设哪家好网站建设 织梦者
  • 怎么创建收费网站宁夏住房和城乡建设部网站
  • 怎么确认网站是什么语言做的用php和mysql做网站
  • 安徽做网站的公司有哪些星子网络公司