Android --- Kotlin学习之路:协程的利用,什么是协程,为什么要用协程?( ...

打印 上一主题 下一主题

主题 517|帖子 517|积分 1551

Kotlin 协程(coroutine)学习
以下干货满满,掌握以下内容一定会对你在项目开发中有所资助,记得收藏!!!

  
什么是协程,为什么要用协程?

协程是基于线程,它是轻量级的线程
java用的是线程,kotlin用的是协程,协程是个并发管理工具,定位跟线程实在是一样的。
实在就是把java的线程包了一层。之前我们做网络哀求的时间,想把网络哀求的结果响应在主线程中,应该怎么写?
一般情况下我们会在主线程去开启一个子线程发起网络哀求,然后将哀求的结果在主线程显示
比如Retrofit,会帮我们实现主动在子线程去发起网络哀求,不消我们自己new Thread,然后
会把结果通过onResponse的情势传递给我们,这个时间Retrofit也帮我们切到了主线程,而协程并不须要写回调了。
另外,比如我们想要先开启两个并行协程,然后须要最终归并到主线程,协程是实现是最简单的。
  1. interface ApiService {
  2.     @GET(value = "posts/1")
  3.     fun queryUser(@Query(value = "userId", encoded = true) userId: String) : Call<UserResponse>
  4.     @GET(value = "user")
  5.     suspend fun getUser(@Query(value = "userid", encoded = true) username:String) :UserResponse
  6. }
复制代码
老的写法:
  1. // Retrofit 请求
  2. val create = Retrofit.create(ApiService::class.java)
  3. create.queryUser("1").enqueue(object : Callback<UserResponse> {
  4.         override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
  5.                 if (response.isSuccessful) {
  6.                         val body = response.body()
  7.                         body?.let {
  8.                                 Log.i("Retrofit", "get success : $it")
  9.                         }
  10.                 }
  11.         }
  12.         override fun onFailure(call: Call<UserResponse>, t: Throwable) {
  13.                 Log.e("Retrofit", "get onFailure : ${t.message}")
  14.         }
  15. })
复制代码
协程的写法:
  1. CoroutineScope(Dispatchers.Main).launch { // 主线程开启一个协程
  2.         val create = Retrofit.create(ApiService::class.java)
  3.         val user = create.getUser("zhangqi") // 这里调用的是一个挂起函数,挂起函数会在后台执行,原理是协程的Dispatchers.Main自己有线程池
  4.         // 这一行可以做页面刷新,这行操作自动在主线程中执行,原理是,挂起函数会调用handler.post切换到主线程的
  5. }
复制代码
挂起函数挂起的是什么?

挂起的是协程,挂起之后,协程就会脱离当前线程,等待挂起函数实行完成之后再切返来
挂起函数中的suspend 关键字的作用是什么?

提示函数调用者,我是一个耗时函数,须要放在背景运行,请在协程中调用我
suspend 只是提示
假如你想在协程中实行串行的代码,而不是并行的,怎么处理?

须要利用withContext
  1. CoroutineScope(Dispatchers.Main).launch {
  2.         launch {
  3.                 println("--1-- ${Thread.currentThread().name}")
  4.         }
  5.         println("--2-- ${Thread.currentThread().name}")
  6. }
  7. 上面的代码输出结果:
  8. --2-- main
  9. --1-- main
复制代码
那假如我们想要按照顺序输出,就须要这样写
  1. CoroutineScope(Dispatchers.Main).launch {
  2.         withContext(Dispatchers.IO) {
  3.                 println("--1-- ${Thread.currentThread().name}")
  4.         }
  5.         println("--2-- ${Thread.currentThread().name}")
  6. }
  7. 输出结果:
  8. --1-- DefaultDispatcher-worker-1
  9. --2-- main
复制代码
由于withContext将线程切到了IO线程,所以后面的操作要等待withContext里面的内容实行完才能实行
怎么样写一个自定义挂起函数?

本质上没有自定义挂起函数,就是利用suspend关键字,
然后用挂起函数withContext(){}将你的耗时内容包裹起来
并行协程的启动和交互

  1. lifecycleScope.launch {
  2.         // deferred1 和 deferred2 并行
  3.         val deferred1 = async {println("Test1 ${Thread.currentThread().name}") }
  4.         val deferred2 = async {println("Test2 ${Thread.currentThread().name}") }
  5.         showContributors(deferred1.await() + deferred2.await()) // 将上面两个协程的结果合并
  6. }
复制代码
什么是耗时操作?

文件读写,网络哀求,等待操作(delay(10))等等,都是耗时操作
挂起和壅闭的区别,什么叫挂起,什么叫壅闭?

挂起就相称于delay(12000)
壅闭就相称于Thread.sleep(12000)
挂起之后你可以干其他事,但是壅闭之后你就要等,不能干其他事
什么叫挂起:当前协程不再占用它正在工作的这个线程
协程所利用的调度器

Dispatcher.Main 主线程,底层照旧用原生handler.post()方法切到主线程的
Dispatcher.IO 非主线程 ,底层有自己的Executor管理
Dispacther.Default 非主线程,底层有自己的Executor管理
Dispacther.Unconfiend 基本不会用到
DEFAULT和IO的区别:
DEFAULT 的线程数目是CPU的焦点数相等,面向计算密集型任务
IO 是固定的64个线程的线程池,面向I/O密集型型任务(读写文件,网络访问),CPU基本不工作,I/O的磁盘在做的
协程能够通过挂起函数实现切换线程之后切返来
定义协程必须指定其 CoroutineScope,常用的相干API有:

GlobalScope,生命周期是process级别的,顶级的,纵然Activity或Fragment已经被烧毁了,协程仍然在实行
MainScope, 在Activity中利用,可以在onDestory()中取消协程
viewModelScope,只能在ViewModel中利用,绑定ViewModel的生命周期
lifecycleScope,只能在Activity,Fragment中利用,会绑定Activity和Fragment的生命周期 ,会主动在onDestory 调用自己的cancel()方法取消
协程的启动

通过launch和async构建器启动协程
我们可以用async先启动一个协程,然后在别的协程中通过.await()获取它的结果
  1. lifecycleScope.launch {
  2.         // deferred1 和 deferred2 并行
  3.         val deferred1 = async {println("Test1 ${Thread.currentThread().name}") }
  4.         val deferred2 = async {println("Test2 ${Thread.currentThread().name}") }
  5.         showContributors(deferred1.await() + deferred2.await()) // 将上面两个协程的结果合并
  6. }
复制代码
通过join和await等待作业
launch和async的区别,launch 返回的是一个Job对象,async返回的是一个Defferd对象,Defferd本质也是一个Job
launch常用于不须要返回结果的并发任务,async则常用在须要返回结果的并发任务中
val scope = CoroutineScope(Dispacther.Default)
scope.launch {}
协程的启动模式(指定协程启动之后的一些举动)

DEFAULT:协程创建后,立刻开始调度,在调度前假如协程被取消,其将直接进入取消响应的状态
ATOMIC:协程创建后,立刻开始调度,协程实行到第一个挂起点之前不响应取消
LAZY:只有协程被须要时,包括主动调用协程的start、join或者await等函数时才开始调度,假如调度前就被取消,那么协程将直接进入异常竣事状态
UNDESPATCHED:协程创建后立刻在当前函数调用栈中实行,直到遇到第一个挂起的点
  1. val job = async(context = Dispacther.IO, start = CoroutineStart.UNDESPATCHED){
  2.         println("thread"&{Thread.currentThread().name})
  3. }
  4. //这段代码输出的是主线程
复制代码
协程的作用域构建器

coroutineScope 和 runBlocking
runBlocking 是常规函数,会壅闭线程,coroutineScope是挂起函数。
coroutineScope 一个协程失败了,全部其他其他兄弟协程也会被取消
supervisorScope 一个协程失败了,不会影响其他协程的实行
coroutineScope 和 runBlocking会等待全部的字协程实行完毕,这个作用域才会竣事
Job对象

负责管理协程的生命周期
新创建(New)活泼(Active)完成中(Completing)已完成(Completed)取消中(Cancelling)已取消(Cancelled)
CPU密集型任务的取消

要通过isActive ensureActive() yield 取消
协程取消的副作用

假如在取消协程之后做了一些开释资源的操作,那么就坏菜了。我们可以通过try{}finally{在这里面开释资源,这里的东西一定实行}
还可以用use函数,use函数内部会自己close()资源
超时任务怎么处理

  1. val result = withTimeoutOrNull(1300) {
  2.         repeat(1000) { i ->
  3.                 println("job: I'm sleeping &i")
  4.                 delay(500L)
  5.         }
  6.         "Done"
  7. }?: "Jack"
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

冬雨财经

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表