Android体系课之--Kotlin协程进阶篇-协程中关键知识点梳理(二)

简介: 笔者在写这篇文章之前,也白嫖了很多关于Kotlin协程的文章: 这里笔者将他们分为三种: 1.讲的内容很*浅*,没几句可能就结束了,看完就索然无味了 2.讲的内容很*深*,看到一半就开始晕乎乎了,然后可能还是手机好玩。。 3.内容比较*适中*,读者可以在里面获取到一些协程的基本信息,关键内容可能就浅尝辄止了,很难获取到核心知识点> 知识的学习就像谈恋爱,不能一上来就想和对方深入了解,也不能聊得太浅,影响后续发展,得讲究循序渐进。 接下来笔者会根据由浅入深的方式,阶段性来讲解Kotlin协程相关知识点。读者可以根据自己的需求,选择对应的阶段文章

前言:

笔者在写这篇文章之前,也白嫖了很多关于Kotlin协程的文章: 这里笔者将他们分为三种: 1.讲的内容很,没几句可能就结束了,看完就索然无味了 2.讲的内容很,看到一半就开始晕乎乎了,然后可能还是手机好玩。。 3.内容比较适中,读者可以在里面获取到一些协程的基本信息,关键内容可能就浅尝辄止了,很难获取到核心知识点

知识的学习就像谈恋爱,不能一上来就想和对方深入了解,也不能聊得太浅,影响后续发展,得讲究循序渐进。 接下来笔者会根据由浅入深的方式,阶段性来讲解Kotlin协程相关知识点。读者可以根据自己的需求,选择对应的阶段文章。

笔者主要以下面几个时间线来讲解协程

关于协程的系列文章

Android体系课之--Kotlin协程篇-协程入门 -协程基础用法(一)

Android体系课之--Kotlin协程进阶篇-协程中关键知识点梳理(二)

Android体系课之--Kotlin协程进阶篇-协程的异常处理机制以及suspend关键字详解

Android体系课之--Kotlin协程进阶篇-协程加Retrofit创建一个MVVM模式的网络请求框架(四)

本系列文使用的版本:

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"

// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"

上一篇文章我们对协程的基础用法做了一个总结,主要讲了几点我们来回顾下

协程定义: 1.协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器!)上调度执行,而代码则保持如同顺序执行一样简单。 2.协程是一种并发设计模式,您可以在Android平台上使用它来简化异步执行的代码

协程生命周期

+-----+ start  +--------+ complete   +-------------+  finish  +-----------+
| New | -----> | Active | ---------> | Completing  | -------> | Completed |
+-----+        +--------+            +-------------+          +-----------+
                 |  cancel / fail       |
                 |     +----------------+
                 |     |
                 V     V
             +------------+                           finish  +-----------+
             | Cancelling | --------------------------------> | Cancelled |
             +------------+                                   +-----------+

协程的几种创建方式及返回值解析 runBlocking:阻塞创建协程的线程,并运行协程作用域中的job任务 launch:不阻塞创建线程,返回一个Job,且协程没有被挂起,任务执行中可以被异步取消 async:不阻塞创建线程,且协程没有被挂起,返回一个DeferredCoroutine,此时协程状态为Active,

也还简单介绍了协程作用域协程调度器挂起函数suspend等知识点,下面我们来详细讲解下协程中的几点关键知识点

协程知识点清单:

  • 1.协程调度器CoroutlineDispatcher
  • 2.协程上下文CoroutlineContext
  • 3.协程启动模式CoroutlineStart
  • 4.协程作用域CoroutlineScope
  • 5.挂起函数以及suspend关键字的使用

下面我们依清单来讲解

1.协程调度器CoroutlineDispatcher:

调度器其实就是指定我们的协程任务Job在哪个线程上执行:协程给我们内置了几个调度器:

  • Dispatchers.Default:默认调度器,CPU密集型任务调度器,适合处理后台计算,和rxjava中的Computed调度器一样
  • Dispatchers.IO:适合处理IO操作,如网络读取,文件读取等操作
  • Dispatchers.Main:主线程执行,UI线程,适合需要在UI线程上操作的,如更新UI等
  • Dispatchers.Unconfined:非受限调度器,又或者称为“无所谓”调度器,不要求协程执行在特定线程上

如果我们需要在实现一个功能:如在IO线程读取网络数据,在UI线程更新获取的UI数据,如何操作? 协程内部给我们定义了一个挂起函数:withContext 可以在协程内部切换线程

GlobalScope.launch(Dispatchers.Main) {
    val result = withContext(Dispatchers.IO) {
        //网络请求...
        "请求结果"
    }
    btn.text = result
}

2.协程上下文CoroutlineContext

它是一个包含了用户定义的一些各种不同元素的Element对象集合。 其中主要元素是 Job、 协程调度器CoroutineDispatcher、 还有包含协程异常CoroutineExceptionHandler拦截器ContinuationInterceptor、 协程名CoroutineName等。 这些数据都是和协程密切相关的,每一个Element都一个唯一key

我们来看下他的源码

public interface CoroutineContext {
 public operator fun <E : CoroutineContext.Element> get(key: Key<E>): E? //`1
public fun <R> fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R //4

public operator fun plus(context: CoroutineContext): CoroutineContext =
    if (context === EmptyCoroutineContext) this else context.fold(this) { ...}//2

public fun minusKey(key: Key<*>): CoroutineContext//3

//注意这里,这个key很关键
public interface Key <E : CoroutineContext.Element>

 public interface Element : CoroutineContext {
    public val key: Key<*>

    public override operator fun <E : Element> get(key: Key<E>): E? =
        if (this.key == key) this as E else null

    public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
        operation(initial, this)

    public override fun minusKey(key: Key<*>): CoroutineContext =
        if (this.key == key) EmptyCoroutineContext else this
     }
}
  • 1处可以看出CoroutineContext是一个键值对形式的结构,key为他的内部类Key,而value为他的内部类Element的实例
  • 2处重写了CoroutineContext的+法操作符,这个操作符的重写类似于C++中对数学操作符的重写,外部可以直接使用+法对CoroutineContext进行操作,如相加
  • 3处减法同2中加法原理
  • 4处fold其实是一个对context进行轮询的操作

下面来看下上下文是如何创建的以及什么时候用到: 我们使用launch操作来看:

 public fun CoroutineScope.launch(
      context: CoroutineContext = EmptyCoroutineContext,
      start: CoroutineStart = CoroutineStart.DEFAULT,
      block: suspend CoroutineScope.() -> Unit
  ): Job {
      val newContext = newCoroutineContext(context)//1
      val coroutine = if (start.isLazy)
          LazyStandaloneCoroutine(newContext, block) else
          StandaloneCoroutine(newContext, active = true)
      coroutine.start(start, coroutine, block)
      return coroutine
  }
  //进入1处:
  public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
      val combined = coroutineContext + context //2
      val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined 
      return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
          debug + Dispatchers.Default else debug
  }
  //在2处coroutineContext=EmptyCoroutineContext的实例,调用了+法操作符
  public operator fun plus(context: CoroutineContext): CoroutineContext =
          if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
              context.fold(this) { acc, element ->
                  val removed = acc.minusKey(element.key)
                  if (removed === EmptyCoroutineContext) element else {
                      // make sure interceptor is always last in the context (and thus is fast to get when present)
                      val interceptor = removed[ContinuationInterceptor]
                      if (interceptor == null) CombinedContext(removed, element) else {
                          val left = removed.minusKey(ContinuationInterceptor)
                          if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                              CombinedContext(CombinedContext(left, element), interceptor)
                      }
                  }
              }

根据加法操作符的实现:最后返回的是一个CombinedContext的实例对象,内部包含了n个CoroutineContext.Element的子集

上面提到的 CoroutineId(协程ID),CoroutineName(协程名称),CoroutineDispatcher(协程调度器), ContinuationInterceptor(协程拦截器),CoroutineExceptionHandler(协程异常处理) 等都继承自CoroutineContext.Element 也就是说这些操作都可以使用+,-法进行操作,放入到一个协程的上下文中,组合成一个协程的上下文内容 加法:如果有这个key则,将覆盖原来key中对应的值,如果没有,则追加进入 减法:如果对应的值的key存在于这个context中,则删除这个key对应的值

例子:

fun testCoroutineContext(){
    val coroutineContext1 = Job() + CoroutineName("这是第一个上下文")
    println("coroutineContext1$coroutineContext1")
    val  coroutineContext2 = coroutineContext1 + Dispatchers.Default + CoroutineName("这是第二个上下文")
    println("coroutineContext2$coroutineContext2")
    val coroutineContext3 = coroutineContext2 + Dispatchers.Main + CoroutineName("这是第三个上下文")
    println("coroutineContext3$coroutineContext3")
}
结果:
D/coroutineContext1: [JobImpl{Active}@21a6a21, CoroutineName(这是第一个上下文)]
D/coroutineContext2: [JobImpl{Active}@21a6a21, CoroutineName(这是第二个上下文), Dispatchers.Default]
D/coroutineContext3: [JobImpl{Active}@21a6a21, CoroutineName(这是第三个上下文), Dispatchers.Main]

可以看到第二个context覆盖了第一个context的CoroutineName,并增加了Dispatchers.Default 第三个覆盖了第二个context的CoroutineName和Dispatchers.Main调度器

协程上下文讲解的比较详细点,是因为他贯穿了整个协程体系

3.协程启动模式CoroutlineStart

协程启动模式,顾名思义就是何时执行协程任务 CoroutineStart启动模式是启动协程的时候,传入的第二个参数,这个参数有4中模式

  • DEFAULT:默认启动模式,这个模式下协程启动之后并不是立即执行,有可能在异步操作中被提前终止
  • LAZY:懒汉模式:启动协程的时候不会立即执行协程,等待我们主动调用Job的start,join或者await的时候才会执行协程代码
  • ATOMIC:和DEFAULT一样也是启动协程后立即执行任务,区别是在协程执行到第一个挂起点之前是不会响应Cancel操作,一定要在挂起后才有意义
  • UNDISPATCHED :和ATOMIC一样,区别是UNDISPATCHED在执行到第一个挂起点之前,和启动协程的线程是同一个,只有在挂起后,协程的运行线程才是协程指定的线程

我们用一个例子来看:

fun testCoroutineStart(){
     val defaultJob = GlobalScope.launch{
         println("CoroutineStart.DEFAULT")
     }
     defaultJob.cancel()
     val lazyJob = GlobalScope.launch(start = CoroutineStart.LAZY) {
         println("CoroutineStart.LAZY")
     }
     val atomicJob = GlobalScope.launch(start = CoroutineStart.ATOMIC) {
        println("CoroutineStart.ATOMIC-挂起前")
        delay(300)
        println("CoroutineStart.ATOMIC-挂起后")
     }
    atomicJob.cancel()
    val unDispatched = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
        println("CoroutineStart.UNDISPATCHED-挂起前")
        delay(300)
        println("CoroutineStart.UNDISPATCHED-挂起后")
    }
    unDispatched.cancel()

    println("${Thread.sleep(2000)}:休眠2s后,启动lazyJob")
    lazyJob.start()
}
//结果:
CoroutineStart.DEFAULT
CoroutineStart.ATOMIC-挂起前
CoroutineStart.UNDISPATCHED-挂起前
kotlin.Unit:休眠2s后,启动lazyJob
CoroutineStart.LAZY
//也有可能
CoroutineStart.ATOMIC-挂起前
CoroutineStart.UNDISPATCHED-挂起前
kotlin.Unit:休眠2s后,启动lazyJob
CoroutineStart.LAZY

分析这个例子

我们在启动default模式协程后,调用了cancel,这个时候如果协程执行没有获取到cpu资源,则会直接取消执行 在启动lazy模式的时候,延迟了2s再启动,可以发现,在延迟启动前是不会执行任务的,在调用了start把协程挂起的时候开始执行协程 在启动atomic的时候,我们在协程内部调用了delay将函数挂起,按模式要求,在挂起前是不会受cancel影响,只有在挂起后,job的cancel才有意义,所以不会打印出挂起后的状态

在启动unDispatched模式的时候,和atomic模式一样,在内部调用delay将函数挂起,发现和atomic结果一样 那unDispatched特殊点在哪里呢。 我们来看另外一个例子:

fun testUnDispatched(){
    val x = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED){
        println("挂起前-执行协程的线程id:${Thread.currentThread().id}")
        delay(200)
        println("挂起后-执行协程的线程id:${Thread.currentThread().id}")

    }
    println("启动协程的线程id:${Thread.currentThread().id}")
    Thread.sleep(500)
}

打印结果: 挂起前-执行协程的线程id:1 启动协程的线程id:1 挂起后-执行协程的线程id:12

可以看到:在不指定协程运行的线程的情况下,在调用挂起函数之前协程默认使用的是主线程和启动协程的线程一致 我们改下代码: 增加调度器:Dispatchers.Main

val x = GlobalScope.launch(context = Dispatchers.Main,start = CoroutineStart.UNDISPATCHED){
    println("挂起前-执行协程的线程id:${Thread.currentThread().id}")
    delay(200)
    println("挂起后-执行协程的线程id:${Thread.currentThread().id}")

}
println("启动协程的线程id:${Thread.currentThread().id}")
Thread.sleep(500)
打印结果:
挂起前-执行协程的线程id:1
启动协程的线程id:1
挂起后-执行协程的线程id:1

可以看到指定线程为主线程后走的是和挂起前的线程一致都是在主线程上 根据上面例子总结CoroutineStart.UNDISPATCHED模式:

  • 1.不管指定了调度器与否,挂起前的执行都在当前线程下
  • 2.对于指定了调度器的情况,挂起后的执行都在指定的调度器的线程下
  • 3.如果指定的调度器线程和当前线程一直,则挂起后也会在当前线程下执行

4.协程作用域CoroutlineScope

协程作用域分为三种: 1.顶级作用域 没有父协程的协程所在的作用域 2.协同作用域 子协程所在的作用域,当子协程出现异常,会将异常事件场地给父协程,如果父协程也被取消,则所以的子协程也会被取消 3.主从作用域 也是子协程所在的作用域,区别是子协程作用域出现异常不会,不会传递给父协程,所以也不会引起其他子协程被取消

PS:对于父协程作用域需要等待所以的子协程作用域完成的时候才会变为Completed状态

子协程会继承父协程的上下文,如果子协程的上下文在父协程中存在,则会覆盖父协程的上下文 来看个例子:

val x1 = GlobalScope.launch (context = Dispatchers.IO,start = CoroutineStart.ATOMIC){
 println("scope1:$coroutineContext")
 val x2 = launch(Dispatchers.Default+CoroutineName("第二个协程")) {
 println("scope2:$coroutineContext")
 }
 val x3 = launch(CoroutineName("第三个协程")) {
 println("scope3:$coroutineContext")
 }
}
结果:
scope1:[CoroutineId(1), "coroutine#1":StandaloneCoroutine{Active}@7525500, Dispatchers.IO]
scope2:[CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Active}@18668527, Dispatchers.Default]
scope3:[CoroutineName(第三个协程), CoroutineId(3), "第三个协程#3":StandaloneCoroutine{Active}@5cfdb4c8, Dispatchers.IO]

上面可以看出:

  • scope3继承额scope1的IO调度器,scope2因为设置了默认调度器,所以会覆盖scope1中的。
  • scope2和3设置了CoroutineName,会在1的基础上新增name的context

前面提到协同作用域取消的时候,会传递给父协程,如果父协程取消,所有子协程也会被取消 再来看个例子:

val e1 = CoroutineExceptionHandler { coroutineContext, throwable ->
    println("exception:+$coroutineContext[CoroutineName] ${throwable.message}")
}
val x1 = GlobalScope.launch (Dispatchers.IO+e1,start = CoroutineStart.ATOMIC){
    println("scope1:$coroutineContext")
    val x2 = launch(Dispatchers.Default+CoroutineName("第二个协程")) {
        println("scope2:$coroutineContext")
        throw NullPointerException("testexception")
    }
    val x3 = launch(CoroutineName("第三个协程")) {
        println("scope3:$coroutineContext")
    }
}
打印结果:
scope1:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@210f1e7a, CoroutineId(1), "coroutine#1":StandaloneCoroutine{Active}@4c0cd601, Dispatchers.IO]
scope2:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@210f1e7a, CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Active}@1b89f68, Dispatchers.Default]
exception:+[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@210f1e7a, CoroutineId(1), "coroutine#1":StandaloneCoroutine{Cancelling}@4c0cd601, Dispatchers.IO][CoroutineName] testexception
  • 可以看到父协程接收到了子协程的异常,且并没有打印出scope3
  • 结论:子协程将异常传递给父协程,并终止其他子协程
  • 对于主从作用域,子协程不会将异常传递给父协程:使用supervisorScope或者使用supervisorJob创建协程上下文 我们来看个例子

    val x1 = GlobalScope.launch (Dispatchers.IO+e1,start = CoroutineStart.ATOMIC){
        supervisorScope {
            println("scope1:$coroutineContext")
            val x2 = launch(Dispatchers.Default+CoroutineName("第二个协程")) {
                println("scope2:$coroutineContext")
                throw NullPointerException("testexception")
            }
            val x3 = launch(CoroutineName("第三个协程")) {
                println("scope3:$coroutineContext")
            }
        }
        val x3 = launch(CoroutineName("第四个协程")) {
            println("scope4:$coroutineContext")
        }
    }
    打印结果:
    scope1:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@478f98c0, CoroutineId(1), "coroutine#1":SupervisorCoroutine{Active}@3b54e532, Dispatchers.IO]
    scope2:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@478f98c0, CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Active}@1b89f68, Dispatchers.Default]
    exception:+[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@478f98c0, CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Cancelling}@1b89f68, Dispatchers.Default][CoroutineName] testexception
    scope3:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@478f98c0, CoroutineName(第三个协程), CoroutineId(3), "第三个协程#3":StandaloneCoroutine{Active}@276104b, Dispatchers.IO]
    scope4:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@478f98c0, CoroutineName(第四个协程), CoroutineId(4), "第四个协程#4":StandaloneCoroutine{Active}@4d7eff18, Dispatchers.IO]

可以看到scope2异常的时候,但是并没有终止其他子协程,其他子协程还是正常执行了,且打印出了父协程的异常,那是不是就是说异常事件传递给了父协程了 我们再来看个例子:


val e1 = CoroutineExceptionHandler { coroutineContext, throwable ->
 println("exception1:+$coroutineContext[CoroutineName] ${throwable.message}")
}
val e2 = CoroutineExceptionHandler { coroutineContext, throwable ->
 println("exception2:+$coroutineContext[CoroutineName] ${throwable.message}")
}
val x1 = GlobalScope.launch (Dispatchers.IO+e1,start = CoroutineStart.ATOMIC){
 supervisorScope {
 println("scope1:$coroutineContext")
 val x2 = launch(e2+Dispatchers.Default+CoroutineName("第二个协程")) {
 println("scope2:$coroutineContext")
 throw NullPointerException("testexception")
 }
 val x3 = launch(CoroutineName("第三个协程")) {
 println("scope3:$coroutineContext")
 }
 }
 val x3 = launch(CoroutineName("第四个协程")) {
 println("scope4:$coroutineContext")
 }

}
打印结果:
scope1:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@54940182, CoroutineId(1), "coroutine#1":SupervisorCoroutine{Active}@17d164e8, Dispatchers.IO]
scope2:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$2@29942327, CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Active}@2753c865, Dispatchers.Default]
exception2:+[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$2@29942327, CoroutineName(第二个协程), CoroutineId(2), "第二个协程#2":StandaloneCoroutine{Cancelling}@2753c865, Dispatchers.Default][CoroutineName] testexception
scope3:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@54940182, CoroutineName(第三个协程), CoroutineId(3), "第三个协程#3":StandaloneCoroutine{Active}@74d71d98, Dispatchers.IO]
scope4:[com.allinpay.testkotlin.CoroutlineScope$Companion$testCoroutineStart$$inlined$CoroutineExceptionHandler$1@54940182, CoroutineName(第四个协程), CoroutineId(4), "第四个协程#4":StandaloneCoroutine{Active}@fa99c8a, Dispatchers.IO]

设置了scope2的异常之后,并没有打印父协程中的e1异常,可以看出子协程并没有将异常事件传递给子协程,那为什么前面例子中会出现打印出父协程的异常呢? 相信仔细看了前面内容的知道答案:子协程scope2继承了父协程的e1异常,所以打印出了父协程的异常

5.挂起函数以及suspend关键字的使用

我们先给个例子:

val x5 = GlobalScope.launch(Dispatchers.Unconfined) {
    println("scope1-挂起前:${Thread.currentThread().id} $coroutineContext")
    val s = test1()
    println("s:$s")
    withContext(Dispatchers.IO){
        println("scope2:${Thread.currentThread().id} $coroutineContext")
        delay(1000)
    }
    println("scope1-挂起后:${Thread.currentThread().id}")
}
println("after")
打印结果:
scope1-挂起前:1 [CoroutineId(1), "coroutine#1":StandaloneCoroutine{Active}@3ab39c39, Dispatchers.Unconfined]
run test1:1
after
s:test1 return
scope2:12 [CoroutineId(1), "coroutine#1":DispatchedCoroutine{Active}@699bd409, Dispatchers.IO]
scope1-挂起后:14

在一个协程内部调用了withContext的挂起函数,看协程挂起前后的结果可知,挂起前和挂起后的代码是走在两个线程里面, 就是说,调用挂起函数后,并没有阻塞挂起前协程的代码,而是继续运行着,而调用挂起函数,恢复协程后,启动了一个新的线程去执行了代码。 由上面分析可知,调用挂起函数后,只是暂停了协程的执行,并没有阻塞原有线程,待挂起函数运行结束后,启动一个新的线程或者原线程空闲状态使用原线程进行处理。 用哪个线程取决于使用的是哪个调度器和cpu的处理

对挂起函数做个总结

挂起函数是在不阻塞原有线程的情况下,将协程挂起,并在挂起函数结束回调resumeWith回复协程的时候, 使用原线程或者启用一个新的协程去处理,具体使用哪个取决于传入的调度器和cpu, 挂起函数一定只能在挂起函数或者协程内部使用,如果在协程外部使用且没有在挂起函数内部使用,则编译器会报错,因为挂起函数就是为了把协程挂起而做的, 使用bytecode编译可以发现挂起函数编译后会有个协程的参数,且这个参数不能为null

总结:

本篇文章对协程的几个关键知识点做了总结,看完后相信你对协程会有更深刻的理解
下一篇文章笔者会讲解一些关于协程中异常处理的机制
关于协程的系列文章

Android体系课之--Kotlin协程篇-协程入门 -协程基础用法(一)

Android体系课之--Kotlin协程进阶篇-协程中关键知识点梳理(二)

Android体系课之--Kotlin协程进阶篇-协程的异常处理机制以及suspend关键字详解

Android体系课之--Kotlin协程进阶篇-协程加Retrofit创建一个MVVM模式的网络请求框架(四)

相关文章
|
18小时前
|
移动开发 安全 Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【5月更文挑战第21天】 在移动开发领域,尤其是针对Android平台,网络请求的处理一直是影响应用性能和用户体验的关键因素。随着现代应用对实时性和响应速度要求的不断提高,传统的同步阻塞或异步回调模式已不再满足开发者的需求。本文将探讨利用Kotlin协程来简化Android应用中的网络请求处理,实现非阻塞的并发操作,并提升应用的整体性能和稳定性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android项目中集成和优化网络请求流程。
|
20小时前
|
移动开发 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第21天】在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着Kotlin语言在Android平台的广泛采纳,其并发处理的强大工具—协程(Coroutines),已成为提升应用响应性和效率的关键因素。本文将深入分析Kotlin协程的核心原理,探讨其在Android开发中的优势,并通过实例演示如何有效利用协程来优化应用性能,打造更加流畅的用户体验。
8 4
|
1天前
|
安全 Java Android开发
构建高效Android应用:Kotlin与Jetpack的实践指南
【5月更文挑战第20天】 在移动开发的世界中,效率和性能始终是开发者追求的核心目标。随着技术的不断进步,Kotlin语言以其简洁、安全和实用的特性成为了Android开发的首选语言。与此同时,Android Jetpack组件的推出,为开发者提供了一套高质量的库、工具和指南,以简化应用程序的开发过程。本文将探讨如何结合Kotlin语言和Jetpack组件来构建一个高效的Android应用,涵盖从项目初始化到性能优化的全过程。
|
1天前
|
调度 数据库 Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第20天】 在移动开发领域,性能优化和流畅的用户体验始终是开发者追求的核心目标。随着Kotlin语言的普及,其提供的协程功能已经成为实现这一目标的重要工具。本文将深入探讨Kotlin协程在Android开发中的应用优势,并通过实例代码演示如何在应用中有效利用协程进行异步编程、网络请求和数据库操作。通过这些实践,开发者可以更好地理解并运用协程,以提升应用的性能和响应速度。
|
1天前
|
测试技术 Android开发 开发者
构建高效Android应用:Kotlin协程与Flow的完美融合
【5月更文挑战第20天】 在现代Android开发中,提升应用性能和用户体验是至关重要的任务。Kotlin作为一种现代化的编程语言,以其简洁、安全和易于理解的特点被广泛采用。特别是Kotlin协程和Flow这两个特性,它们为处理异步任务和数据流提供了强大而灵活的工具。通过深入探索Kotlin协程和Flow的结合使用,本文将揭示如何利用这些特性构建更加高效且响应迅速的Android应用。我们将探讨实现细节,以及如何通过这种技术堆栈来优化资源管理和用户界面的流畅度。
|
3天前
|
移动开发 Android开发 开发者
构建高效安卓应用:Kotlin 协程的实践指南
【5月更文挑战第18天】 随着移动开发技术的不断进步,安卓平台亟需一种高效的异步编程解决方案来应对日益复杂的应用需求。Kotlin 协程作为一种新兴的轻量级线程管理机制,以其简洁的语法和强大的功能,成为解决这一问题的关键。本文将深入探讨Kotlin协程在安卓开发中的实际应用,从基本概念到高级技巧,为开发者提供一份全面的实践指南,旨在帮助读者构建更加高效、稳定的安卓应用。
|
4天前
|
移动开发 Android开发 UED
构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第17天】 在移动开发领域,尤其是针对Android平台,性能优化和流畅的用户体验始终是开发者追求的目标。Kotlin作为一种现代的编程语言,自引入Android开发以来,其简洁、安全和互操作性的特点受到广泛欢迎。特别是Kotlin协程的推出,为解决Android平台上的并发编程问题提供了新的思路。本文将深入探讨Kotlin协程的核心优势,并通过实例展示如何在Android应用中有效利用协程来提高响应性和稳定性,从而改善整体的用户体验。
|
Java
Kotlin从入门到放弃(三)——协程
引言 这篇主要是将以下kotlin里面的协程,当然这个概念已经随着kotlin的文档被广泛得知了,不过还是用大量代码记录一下吧 一、概念    Coroutine,翻译为协程,意思为各个子任务程协作运行。
2224 0
|
4天前
|
Android开发 Kotlin API
Android插件化探索与发现,kotlin协程切换线程
Android插件化探索与发现,kotlin协程切换线程