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

kotlin中协程相关

协程

  • 用同步的方式写出异步的效果
  • 协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式
  • 挂起函数(suspend)不一定就是在子线程中执行的,但是通常在定义挂起函数时都会为它指定其他线程,这样挂起才有意义
  • 解决多层嵌套回调

协程不是线程,是基于线程封装的库,可以使用协程库提供的API方便的灵活的指定协程中代码执行的线程、切换线程,但不需要接触线程Thread类。类似于Android的AsyncTask或者RxJava的Schedulers,都解决了异步线程切换的问题,然而协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式,把原本运行在不通线程的代码写在一个代码块{}里,开起来就像是同步代码。

launchasync之间的区别

launch是一种适用于“发射并忘记”场景的协程构建器,当你不需要等待协程的结果时,它非常有用。它允许你在后台异步执行任务,而不会阻塞主线程,从而提高应用的响应性。

示例:使用launch

fun main() {println("Before launch")// 启动一个协程GlobalScope.launch {delay(1000)println("Inside launch")}println("After launch")Thread.sleep(2000)
}

在上面的示例中,我们使用launch关键字在GlobalScope中启动了一个协程。协程通过delay()函数暂停了1000毫秒,然后打印了"Inside launch"。同时,主线程继续执行。输出结果为:Before launch      After launch     Inside launch

async是一种用于异步执行任务并获取其结果的协程构建器。它返回一个Deferred<T>对象,表示将来可用的值

示例:使用async并发进行网络请求

suspend fun fetchDataFromAPI(url: String): String {// 执行网络请求或其他耗时操作return apiService.fetchData(url)
}suspend fun fetchMultipleData() {val deferredData1 = GlobalScope.async(Dispatchers.IO) {fetchDataFromAPI("https://api.example.com/data1")}val deferredData2 = GlobalScope.async(Dispatchers.IO) {fetchDataFromAPI("https://api.example.com/data2")}val data1 = deferredData1.await()val data2 = deferredData2.await()// 处理获取到的数据```kotlinprocessData(data1, data2)
}

在这个示例中,我们使用async来并发地从多个URL获取数据。每个网络请求被封装在一个单独的async块中,它返回一个Deferred<String>对象。然后我们使用await()来在结果可用时获取结果。获取到数据后,我们可以根据需要进行进一步处理。

总结选择launchasync的关键考虑因素:

  • 当你想要执行一个异步任务而不需要等待结果时,例如进行网络请求或执行后台操作时,使用launch

  • 当你需要并发地执行多个异步任务并获取它们的结果以进行进一步处理时,例如并行网络请求或计算时,使用async

  • 要注意选择合适的上下文来启动协程,以确保正确的线程管理。对于与UI相关的操作,切换到Dispatchers.Main上下文来更新UI。

  • 避免使用长时间运行的操作阻塞主UI线程。使用协程将这些任务转移到后台线程,从而确保响应性用户界面

suspend:withContext(Dispatchers.IO)异步线程

MainScope   需要销毁在onDestroy()方法中:main.cancel()

GlobalScope 全局作用域 默认是异步线程Dispatchers.IO

viewModelScope.launch 默认是主线程Dispatchers.Main

一个页面请求多个接口示例

class SystemViewModel : BaseViewModel() {private val remoteRepository: SystemRemoteRepository by lazy {SystemRemoteRepository()}val page = MutableLiveData<Pagination<Article>>()fun getArticleList() {viewModelScope.launch { //主线程开启一个协程// 网络请求:IO线程val tree: ApiResult<MutableList<Tree>> =RetrofitClient.apiService.getTreeByCoroutines()// 主线程val cid = tree?.data?.get(0)?.idif (cid != null) {// 网络请求:IO线程val pageResult: ApiResult<Pagination<Article>> =RetrofitClient.apiService.getArticleListByCoroutines(0, cid)// 主线程page.value = pageResult.data!!}}}
}/**接口定义,Retrofit从2.6.0版本开始支持协程*/
interface ApiService {/*获取文章树结构*/@GET("tree/json")suspend fun getTreeByCoroutines(): ApiResult<MutableList<Tree>>/*根据数结构下某个分支id,获取分支下的文章*/@GET("article/list/{page}/json")suspend fun getArticleListByCoroutines(@Path("page") page: Int,@Query("cid") cid: Int): ApiResult<Pagination<Article>>
}
在定义接口时,方法前加了个 suspend 关键字,调用接口的时候用viewModelScope.launch {} 包裹。既然可以运行成功,就说明请求接口并不是在主线程中进行的,然而有的同学不信,他在getArticleList() 方法中的任意位置通过 Thread.currentThread() 打印的结果都Thread[main,5,main],这不就是在主线程中调用的吗?上述协程中的代码是在主线程执行没错,但是接口请求的方法却是在子线程中执行的。
原因就在于我们定义接口的时候使用了suspend 关键字,它的意思是挂起、暂停,函数被加了这个标记就称它为挂起函数,需要注意的是,suspend 关键字并没有其他重要的作用,它仅仅标识某个函数是挂起函数,可以在函数中调用其他挂起函数,但是只能在协程中调用它。所以上面两个接口都被定义为挂起函数了,挂起函数只能在协程中调用,那谁是协程?
其实在 kotlin 协程库中是有一个类 AbstractCoroutine 来表示协程的,这个抽象类有很多子类代表不同的协程,但是这些子类都是private 的,并没有暴露给我们,所以你在其他文章中看到别人说
viewModelScope.launch{} 包裹起来的闭包 ( 代码块 ) 就是协程也没问题,但这个代码块的真正意义是协程需要执行的代码。当在协程中调用到挂起函数时,协程就会在当前线程(主线程)中被挂起,这就是协程中著名的 非阻塞式挂起,主线程暂时停止执行这个协程中剩余的代码,注意:暂停并不是阻塞等待(否则会ANR ),而是主线程暂时从这个协程中被释放出来去处理其他 Handler 消息,比如响应用户操作、绘制View 等等。
那挂起函数谁执行?这得看挂起函数内部是否有切换线程,如果没有切换线程当然就是主线程执行了,所以挂起函数不一定就是在子线程中执行的,但是通常在定义挂起函数时都会为它指定其他线程,这样挂起才有意义。比如上面定义的suspend 的请求接口, Retorift 在执行请求的时候就切换到了子线程并挂起主线程,当请求完成(挂起函数执行完毕)返回结果时,会通知主线程:我该干的都干完了,下面的事你接着干吧,主线程接到通知后就会拿到挂起函数返回的结果继续执行协程里面剩余的代码,这叫做协程恢复(resume) 。如果又遇到挂起函数就会重复这个过程,直到协程中的代码被执行完。
通过协程可以彻底去除回调,使用同步的方式编写异步代码。什么是同步调用?调用一个方法能直接拿到方法的返回值,尽管这个方法是耗时的、在其他线程执行的,也能直接得到它的返回值,然后再执行下面的代码,协程不是通过等待的方式实现同步,而是通过非阻塞挂起实现看起来同步的效果。
http://www.lryc.cn/news/347474.html

相关文章:

  • (自适应手机端)物流运输快递仓储网站模板 - 带三级栏目
  • Navicat导出表结构到Excel或Word
  • Golang编译优化——稀疏条件常量传播
  • 人工智能培训讲师咨询叶梓介绍及智能医疗技术与ChatGPT临床应用三日深度培训提纲
  • HCIP(BGP综合实验)--8
  • 深入理解C++中的Vector容器:用容器构建高效程序
  • 目标检测YOLO实战应用案例100讲-基于深度学习的交通场景多尺度目标检测算法研究与应用(下)
  • react 类组件 和 函数组件 声明周期 对比
  • 智慧变电站守护者:TSINGSEE青犀AI视频智能管理系统引领行业革新
  • 【Ubuntu20.04安装java-8-openjdk】
  • HTTPS对于网站到底价值几何?
  • Docker私有仓库Harbor
  • 48. 旋转图像/240. 搜索二维矩阵 II
  • wsl安装Xfce桌面并设置系统语言和输入法
  • 短信清空了!华为手机短信删除了怎么恢复?
  • Linux实现Flappy bird项目
  • 【python量化交易】qteasy使用教程07——创建更加复杂的自定义交易策略
  • SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输
  • 力扣每日一题119:杨辉三角||
  • AI语音模型PaddleSpeech踩坑(安装)指南
  • 如何更好地使用Kafka? - 运行监控篇
  • 数据可视化训练第四天(模拟投掷筛子并且统计频次)
  • 4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符
  • (Java)心得:LeetCode——18.四数之和
  • 网络编程套接字详解
  • 蓝桥杯备战11.歌唱比赛
  • 微信小程序中的图像奥秘:图片与Base64的华丽变身记
  • 【35分钟掌握金融风控策略25】定额策略实战2
  • 我和爬虫的故事
  • 常用的简单友好的工单系统(免费)- WGCAT