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

Kotlin 协程 - 在Android中的使用

一、使用场景1.1 LiveData 还是 StateFlowLiveData 问题StateFlow 解决粘性事件(重放)按下Button弹出Toast当配置改变例如屏幕旋转时页面会销毁后重建观察者将再次订阅LiveData此时会再次弹出Toast。一样存在粘性事件问题。事件应该使用SharedFlow它默认回放0额外缓存0。数据不防抖(判重)更新的值和当前值相同onChange()依然会再次调用。可以使用Transformations的distinctUntilChanger()、SingleLiveEvent解决。会判断更新的值与当前值是否相同相同则不更新。只处理最终值(丢失数据)在一个UI刷新周期内连续postValue()更新数据只会显示最后一次的内容。我们希望更新的每一个值事件都会刷新UI而LiveData会丢弃中间值只处理最终值最新状态。setValue不存在该情况一样只关注状态只持有1个最新值。可使用SharedFlow处理事件只在主线程处理更新值的函数都是在主线程回调也是在主线程。协程随意切换线程。生命周期感知只在界面活跃状态下Start和Resume状态接收通知非活跃状态更新UI无意义浪费资源。在UI销毁时Destroy状态自动取消订阅避免内存泄漏。StateFlow执行在协程中lifecycleScope会在UI销毁时结束协程。launchWhenX会在进入X状态前等待、进入后执行、离开后挂起如果内部订阅了数据流挂起只是停止消费数据没有取消协程是无法阻止被订阅的数据流继续活跃生产数据。repeatOnLifecycle会在离开X状态时取消协程再次进入X状态再重开协程即围绕X状态的进出多次重新执行代码。没有默认值创建实例未赋值被调用会异常。构造必须传入初始化值null安全符合页面必须有初始状态的逻辑。1.2 Suspend 还是 Flow除非界面非常简单否则不要用作UI状态流使用如获取数据时往 Flow 里面发送 Loading、Success(value)、Failed(value) 等状态然后在 UI 中收集并判断加载不同界面。界面状态应使用专门的 UiState通过可观察容器StateFlow、LiveData、MutableState暴露给 UI 订阅且优先使用挂起函数。Flow应该纯粹获取值正确的使用场景需要返回多个值应该是轮询如先返回缓存再返回最新数据、超时重试等连续加工等需求。SuspendFlow值的数量一次性异步调用单值。数据流多个值。场景一次性数据例如文章内容。①在Repository中合并多个数据源这些数据可能随时发生变化。②数据随时变化需要观察例如Room等。1.3 哪种数据流FlowStateFlowSharedFlowChannel类型冷流数据只能在创建对象的时候定义生产方式。热流数据可以后期发送到流中。数据的生产消费时才会生产数据就像爱奇艺点播视频。不消费也会生产数据就像广播电台播放内容。数据的接收完整每次消费收到的都是从头开始的完整数据。无订阅者会丢弃值只能接收后续数据。通信无订阅者会挂起等待对方的接收。最新只持有单个且最新的值。历史可以回放历史数据。关系独立多个订阅者彼此之间独立。共享多个订阅者同时接收收到的值相同。分配多个订阅者轮流接收收到的值不是同一个值。关闭流会自动关闭取消协程或数据生产完。构造创建的不会自动关闭转换的启动模式配置为WhileSubscribed会超时关闭。构造函数创建的不会自动关闭协程构建器创建的会跟随协程关闭。二、相互转换2.1 改造回调API2.1.1 Callback → SuspendsuspendCancellableCoroutine对于已有的调用了回调的函数可以改造成挂起函数onSuccess()中调用resume()返回数据onFailure()中调用resumeWithException()返回异常。suspendCoroutine适用于改造SAM回调。public suspend inline fun T suspendCancellableCoroutine(crossinline block: (CancellableContinuationT) - Unit): T//1.回调接口 interface NetCallbackT { fun onSuccess(t: T) fun onError(excpption: Exception) } //2.在Model中封装回调API联网获取数据 fun getData(callback: NetCallbackString) { Thread { Thread.sleep(1000) //模拟联网操作 callback.onSuccess(数据) callback.onError(Exception(错误)) }.run() } //3.以前在ViewModel中封装功能 fun show() { getData(object : NetCallbackString { override fun onSuccess(t: String) {...} override fun onError(excpption: Exception) {...} }) } //4.现在在ViewModel中封装功能 suspend fun showWithSuspend():String suspendCancellableCoroutine { cancellableContinuation - getData(object : NetCallbackString { override fun onSuccess(t: String) { cancellableContinuation.resume(t)} override fun onError(excpption: Exception) {cancellableContinuation.resumeWithException(excpption)} }) }2.1.2 Callback → FlowcallbackFlow底层使用的sendChannel默认容量64满了会挂起直到消费出空位为了避免生产被挂起可以配置为CONFLATED或者UNLIMITED。fun T callbackFlow(block: suspend ProducerScopeT.() - Unit): FlowTsend ()发送数据。offer ()允许在协程外提交。sendBlocking ()尝试用offer失败则用runBlocking{ send() }阻塞式提交。awaitClose ()Flow关闭时执行用来释放资源注销回调函数未调用报错 IllegalStateException。fun showWithFlow(): FlowInt callbackFlow { //1.创建Flow并发送值实现Callback接口 val callback object: NetCallback { override fun onNextValue(value: Int) { try { offer(num) } catch(t: Throwable) {...} } override fun onError(ecxeption: Throwable) { cancel(CancellationException(API发生错误, exception)) } override fun onCompleted() close() } //2.注册回调传参使用并对API进行配置操作 getData(callback) //3.取消协程并注销回调用来释放API资源 awaitClose {...} }2.2 数据转换到LiveData2.2.1 Suspend → LiveDataliveData{ }public fun T liveData(context: CoroutineContext EmptyCoroutineContext,//区块所执行的协程上下文默认Main.immediate。timeoutInMs: Long DEFAULT_TIMEOUT, //没有观察者后多少毫秒后取消区块默认5s。BuilderInference block: suspend LiveDataScopeT.() - Unit //区块在LiveData被观察的时候执行): LiveDataTemit ()提交新值emitSource ()订阅其它LiveData从中获取值。.latestValue获取最新提交的值class MyViewModel : ViewModel() { //直接调用挂起函数赋值 val num1: LiveDataInt liveData { emit(getData()) } //还可以指定线程单独写耗时操作 val num2: LiveDataInt liveData(Dispatchers.IO) { emit(5) delay(1000) emit(3) } //从另一个LivaData获取更新结果 val aa MutableLiveData(10) val bb liveData{ emitSource(aa) } suspend fun getData(): Int withContext(Dispatchers.IO) { 3 } }2.2.2 Flow → LiveDataasLiveDatapublic fun T FlowT.asLiveData(context: CoroutineContext EmptyCoroutineContext,//区块所执行的协程上下文默认Main.immediatetimeoutInMs: Long DEFAULT_TIMEOUT //没有观察者后多少毫秒后取消区块默认5s): LiveDataTclass MyViewModel : ViewModel() { val num1: LiveDataInt liveData { DataSource().getDataFlow.collect { emit(it) } } val num2: LiveDataInt DataSource().getDataFlow.asLiveData() //简写 } class DataSource { val getDataFlow: FlowInt flow { repeat(3) { emit(it) } } }2.3 流之间转换2.3.1 Channel → Flowpublic fun T ReceiveChannelT.receiveAsFlow(): FlowT流能被多次消费。public fun T ReceiveChannelT.consumeAsFlow(): FlowT流只能被消费一次。fun main(): Unit runBlocking { val channel ChannelInt() val receiveAsFlow channel.receiveAsFlow() val consumeAsFlow channel.consumeAsFlow() launch { receiveAsFlow.collect { print($it,) } } repeat(3) { channel.send(it) } channel.close() }2.3.2 Flow → Channelpublic fun T FlowT.produceIn(scope: CoroutineScope): ReceiveChannelTfun main(): Unit runBlocking { val receiveChannel (1..3).asFlow().produceIn(this) repeat(3) { print($i,) } }2.3.3 Flow → SharedFlowpublic fun T FlowT.shareIn(scope: CoroutineScope, //数据共享时所在的协程作用域started: SharingStarted, //启动策略replay: Int 0 //回放新订阅时得到几个之前已经发射过的旧值。): SharedFlowT2.3.4 Flow → StateFlowpublic fun T FlowT.stateIn(scope: CoroutineScope, //数据共享时所在的协程作用域started: SharingStarted, //启动策略initialValue: T //默认值): StateFlowTpublic suspend fun T FlowT.stateIn(scope: CoroutineScope): StateFlowT {val config configureSharing(1)val result CompletableDeferredStateFlowT()scope.launchSharingDeferred(config.context, config.upstream, result)return result.await()}挂起函数版本不用指定默认值会挂起直到产出第一个值。三、UI 生命周期Flow 是通用框架不关心是在什么 UI 框架中订阅不同UI框架专有的生命周期处理也无法感知应用生命周期处于前台后台不会自动停止收集必须在协程中收集还要确保切到主线程中更新UI因此需要特殊处理。通常需要在UI层收集数据流以便显示更新当UI进入后台不可见时数据流也会持续发送不停止除非手动取消协程。如使用了基于Channel收集的CoroutineScope.launch、Flow.launchIn、LifecycleCoroutineScope.launchWhenX或使用了带缓存操作符的Flowbuffer、conflate、flowIn、shareIn。收集单个Flow使用Flow.flowWithLifecyle( )收集多个流、热流使用Lifecyle.repeatOnLifecyle( )不搜集流使用LifecycleScope.launchWhenX( )3.1 Flow.flowWithLifecyle( )当只有一个Flow需要收集时使用该操作符内部基于Lifecyle.repeatOnLifecyle()实现的扩展函数。生命周期低于目标状态会取消上游不影响下游需要注意调用顺序。public fun T FlowT.flowWithLifecycle(lifecycle: Lifecycle,minActiveState: Lifecycle.State Lifecycle.State.STARTED): FlowT callbackFlow {lifecycle.repeatOnLifecycle(minActiveState) {thisflowWithLifecycle.collect {send(it)}}close()}3.2 Lifecyle.repeatOnLifecyle( )在生命周期到达目标状态时挂起调用它的协程。会在离开目标状态时取消子协程再次进入目标状态会重开子协程即围绕目标状态的进出多次重新执行代码。进入Destroy状态才会恢复调用它的协程。public suspend fun Lifecycle.repeatOnLifecycle(state: Lifecycle.State,//可选状态INITIALIZED、CREATED、STARTED、RESUMED、DESTROYEDblock: suspend CoroutineScope.() - Unit)lifecycleScope.launch { //会挂起之前的代码 repeatOnLifecycle(Lifecycle.State.STARTED) { //重复的工作 } //当进入onDestroy()状态协程会恢复执行后面的代码 }3.3LifecycleScope.launchWhenX( )会在进入X状态前等待、进入后执行、离开后挂起。如果内部订阅了数据流挂起只是停止消费数据没有取消协程无法阻止被订阅的数据流继续活跃生产数据由于会造成资源浪费已被废弃使用上面的 3.2 替代。public fun launchWhenCreated(block: suspend CoroutineScope.() - Unit): Jobpublic fun launchWhenResumed(block: suspend CoroutineScope.() - Unit): Jobpublic fun launchWhenStarted(block: suspend CoroutineScope.() - Unit): Job3.4 配置生产者MutableSharedFlowMutableStateFlow持有数据流实例的对象还在内存中它们就会保持生产者的活跃状态。控制字段subscriptionCount为0的时候内部的生产者就会停止。Flow.shareInFlow.stateIn对形参启动策略配置为WhileSubscriibed()会在没有活跃订阅者时停止内部生产者。而无论是配置成Eagerly还是Lazy只要使用的协程作用域还处于活跃状态内部生产者就会保持活跃。

相关文章:

Kotlin 协程 - 在Android中的使用

一、使用场景1.1 LiveData 还是 StateFlowLiveData 问题StateFlow 解决粘性事件(重放):按下Button弹出Toast,当配置改变例如屏幕旋转时,页面会销毁后重建,观察者将再次订阅LiveData,此时会再次弹出Toast。一样存在粘性…...

Windows电脑上直接运行安卓应用?APK安装器终极解决方案

Windows电脑上直接运行安卓应用?APK安装器终极解决方案 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 还在为安卓模拟器的卡顿和资源占用而烦恼吗&#xf…...

全面修复:Windows更新重置工具的完整使用指南

全面修复:Windows更新重置工具的完整使用指南 【免费下载链接】Script-Reset-Windows-Update-Tool This script reset the Windows Update Components. 项目地址: https://gitcode.com/gh_mirrors/sc/Script-Reset-Windows-Update-Tool Script-Reset-Windows…...

PyTDX get_security_list踩坑记:start=8000时数据为空?一个编码问题引发的血案

PyTDX get_security_list深度解析:当start8000时数据异常的背后逻辑 1. 问题现象与初步分析 在量化开发过程中,使用PyTDX库获取深市股票列表时,发现一个诡异现象:当start参数设置为8000时,返回数据为空,而其…...

面试官爱问的二叉树重建:对比‘先序+中序’与‘中序+层序’两种解法(C++实现)

二叉树重建实战:从遍历序列到完整结构的两种经典解法 在技术面试中,二叉树相关的问题几乎成了必考题目。而其中最具代表性的,莫过于根据遍历序列重建二叉树的问题。这类问题不仅考察候选人对二叉树结构的理解程度,更能检验其递归思…...

FutureRestore-GUI:iOS设备降级恢复的专业图形化工具完整指南

FutureRestore-GUI:iOS设备降级恢复的专业图形化工具完整指南 【免费下载链接】FutureRestore-GUI A modern GUI for FutureRestore, with added features to make the process easier. 项目地址: https://gitcode.com/gh_mirrors/fu/FutureRestore-GUI Futu…...

Move Mouse:Windows防休眠的智能管家,让电脑时刻待命

Move Mouse:Windows防休眠的智能管家,让电脑时刻待命 【免费下载链接】movemouse Move Mouse is a simple piece of software that is designed to simulate user activity. 项目地址: https://gitcode.com/gh_mirrors/mo/movemouse 你是否经历过…...

如何用RyzenAdj解锁AMD笔记本隐藏性能?实用电源管理技巧大揭秘

如何用RyzenAdj解锁AMD笔记本隐藏性能?实用电源管理技巧大揭秘 【免费下载链接】RyzenAdj Adjust power management settings for Ryzen APUs 项目地址: https://gitcode.com/gh_mirrors/ry/RyzenAdj RyzenAdj是一款专为AMD Ryzen移动处理器设计的开源电源管…...

别再手动种树了!用Forest Pack Pro预设库5分钟搞定3DMAX森林场景

别再手动种树了!用Forest Pack Pro预设库5分钟搞定3DMAX森林场景 当你在3DMAX中手动摆放第100棵树时,是否开始怀疑人生?那些看似简单的森林场景,往往消耗设计师80%的时间在重复劳动上。Forest Pack Pro的预设库功能,彻…...

从WKS文件看Yocto镜像构建:深度解析i.MX平台Bootloader与分区布局的自动化配置

从WKS文件看Yocto镜像构建:深度解析i.MX平台Bootloader与分区布局的自动化配置 在嵌入式Linux开发领域,Yocto项目已经成为构建定制化Linux发行版的事实标准工具链。对于使用NXP i.MX系列处理器的开发者而言,如何高效地配置启动流程和存储分区…...

ASTRAL物种树构建终极指南:高效处理不完全谱系分选的完整方案

ASTRAL物种树构建终极指南:高效处理不完全谱系分选的完整方案 【免费下载链接】ASTRAL Accurate Species TRee ALgorithm 项目地址: https://gitcode.com/gh_mirrors/ast/ASTRAL 在进化生物学研究中,构建准确的物种树面临着一个核心挑战&#xff…...

R 4.5并行计算终极配置清单(含17个环境变量、9个.Rprofile隐藏指令、5个Makevars强制编译开关)

第一章:R 4.5并行计算优化方法概览R 4.5 引入了对并行计算基础设施的多项底层增强,包括对 parallel 包的线程安全改进、future 框架的原生支持升级,以及对 foreach 与 doParallel 组合执行效率的显著提升。这些变更使得多核 CPU 利用率更稳定…...

别再被‘不是注册脚本’坑了!手把手教你用记事本创建正确的.reg文件(附微信协议关联案例)

从零构建合规注册表脚本:避开.reg文件导入失败的六大陷阱 每次双击精心准备的.reg文件却看到"不是注册脚本"的红色警告,就像在终点线前被绊倒——这种挫败感我深有体会。三年前第一次尝试为团队部署软件环境时,我连续七次遭遇这个错…...

别再只用rand()了!手把手教你用STM32的ADC噪声生成真随机数(附DMA优化方案)

STM32真随机数生成实战:从ADC噪声到安全密钥的完整实现 在嵌入式系统开发中,随机数的质量往往决定了整个系统的安全性。许多开发者习惯性地使用srand(time(NULL))配合rand()函数来生成随机数,却不知道这种伪随机数在安全敏感场景下可能带来灾…...

vue-axios-github源码解析:手把手教你实现401错误自动跳转登录页

vue-axios-github源码解析:手把手教你实现401错误自动跳转登录页 【免费下载链接】vue-axios-github Vue 全家桶 axios 前端实现登录拦截、登出、拦截器等功能 项目地址: https://gitcode.com/gh_mirrors/vu/vue-axios-github vue-axios-github是一个基于Vu…...

别让时钟约束拖后腿!FPGA设计中那些容易被忽略的时序约束细节:虚拟时钟、输入抖动与不确定性设置

别让时钟约束拖后腿!FPGA设计中那些容易被忽略的时序约束细节:虚拟时钟、输入抖动与不确定性设置 在FPGA设计的世界里,时序约束就像是一把双刃剑——用得好可以让你的设计跑得又快又稳,用得不好则可能成为项目进度和性能的绊脚石。…...

react-native-shared-element 性能优化技巧:避免闪烁和提升动画流畅度

react-native-shared-element 性能优化技巧:避免闪烁和提升动画流畅度 【免费下载链接】react-native-shared-element Native shared element transition "primitives" for react-native 💫 项目地址: https://gitcode.com/gh_mirrors/re/re…...

SpringAI实战:5分钟搞定聊天记录查询API,基于ChatMemory的RESTful接口开发

SpringAI实战:5分钟构建高性能聊天记录查询API 最近在开发一个智能客服系统时,我发现聊天记录的快速检索功能对用户体验至关重要。SpringAI的ChatMemory组件恰好提供了简洁高效的存储方案,但如何将其封装成易用的RESTful接口却鲜有完整案例。…...

高性能开源PLC编程平台:OpenPLC Editor工业自动化开发完整解决方案

高性能开源PLC编程平台:OpenPLC Editor工业自动化开发完整解决方案 【免费下载链接】OpenPLC_Editor 项目地址: https://gitcode.com/gh_mirrors/ope/OpenPLC_Editor OpenPLC Editor作为一款基于PLCopen国际标准的开源工业自动化编程平台,为工业…...

别让Claude Skill变‘话痨’:从官方最佳实践看如何写出‘省token’的高效技能

从Claude Skill设计哲学看高效AI交互的成本控制艺术 在AI技术快速迭代的今天,大型语言模型(LLM)的应用已经从简单的对话扩展到复杂的任务自动化。作为这一领域的先驱之一,Claude Skill系统为开发者提供了构建专业化AI能力的平台。然而,随着应…...

别再傻傻分不清:5分钟搞懂通信里的误比特率、误码率、误帧率和误块率(BLER)

通信系统中的错误率指标全解析:从比特到数据块的精准诊断 想象一下你正在网购一件心仪已久的商品,快递过程中可能会发生各种意外:包裹里的某个小零件损坏(比特错误)、整个配件盒丢失(数据块错误&#xff09…...

ITK-SNAP医学图像分割:3步掌握专业级医学影像分析

ITK-SNAP医学图像分割:3步掌握专业级医学影像分析 【免费下载链接】itksnap ITK-SNAP medical image segmentation tool 项目地址: https://gitcode.com/gh_mirrors/it/itksnap 想要在医学影像分析中实现精准分割却无从下手?ITK-SNAP这款开源工具…...

3 shell脚本编程

Shell脚本简介shell脚本是什么?shell脚本是由 shell命令组成 的文本文件。利用shell命令加shell语法,配合正则表达式、管道命令、数据流从定向等写成的纯文本脚本文件。以.sh为后缀为什么要写它?1、自动话重复任务:可以将重复性或…...

MSYS2安装GCC后,你的PATH环境变量可能踩了这些坑(附正确配置方法)

MSYS2安装GCC后PATH环境变量的深度避坑指南 当你在Windows上通过MSYS2安装GCC工具链时,PATH环境变量的配置可能是最容易被忽视却又最关键的一步。许多开发者按照教程安装完成后,在命令行或IDE中调用gcc时仍然会遇到各种问题——命令未找到、版本冲突、工…...

5分钟快速上手:Windows平台APK安装器完整指南

5分钟快速上手:Windows平台APK安装器完整指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否想在Windows电脑上直接运行安卓应用,却不想…...

告别永恒之蓝阴影:安全迁移Samba服务到非标端口的实战记录

企业级Samba服务安全迁移指南:从445端口到高位端口的完整实践 当企业IT管理员在云服务器上部署Samba服务时,往往会遇到一个令人头疼的问题——445端口被运营商封锁。这背后其实源于几年前席卷全球的"永恒之蓝"漏洞事件,该漏洞利用S…...

Lenovo Legion Toolkit:拯救者笔记本的终极性能控制中心

Lenovo Legion Toolkit:拯救者笔记本的终极性能控制中心 【免费下载链接】LenovoLegionToolkit Lightweight Lenovo Vantage and Hotkeys replacement for Lenovo Legion laptops. 项目地址: https://gitcode.com/gh_mirrors/le/LenovoLegionToolkit 想要完全…...

题解:AcWing 1192 奖金

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大家订阅我的专栏:算法…...

Unity 引擎中的 RuntimeInitializeOnLoadMethod 属性解析

在 Unity 游戏开发中,有许多细微但非常重要的特性,其中之一就是 RuntimeInitializeOnLoadMethod 属性。这篇博文将详细探讨这个属性的工作原理,并结合实例解释其在实际开发中的应用。 背景介绍 Unity 引擎虽然主要使用 C# 进行开发,但其核心是基于 C 和 C++ 构建的。这意…...

直播卡顿、首开慢、延时高?别慌!一份超全的排查手册(附FFmpeg/WebRTC实战参数)

直播质量优化全链路实战:从现象定位到参数调优 直播过程中突然出现的卡顿、首开延迟或音画不同步,往往让技术团队如临大敌。不同于点播的事后处理,直播问题的排查需要工程师在分钟级内完成根因定位与修复。本文将构建一套从现象分析到参数调优…...