Kotlin Coroutine(协程) 基本知识

Kotlin Coroutine(协程)系列:
1. Kotlin Coroutine(协程) 简介
2. Kotlin Coroutine(协程) 基本知识bash

这篇文章主要介绍协程中的一些基本概念。网络

挂起函数(suspend关键字)

Kotlin中提供了关键字suspend用来描述一个函数为挂起函数,写法以下:async

//官方提供的函数
suspend fun delay(timeMillis: Long) {
    ...
}
复制代码

以上写法就表明delay函数为一个挂起函数。函数

在前面一篇文章Kotlin Coroutine(协程) 简介中我提到过挂起函数只会挂起当前协程,不会挂起阻塞当前协程所处的线程。事实上,想要执行协程就至少须要一个挂起函数,所以挂起函数是协程中一个很是重要的概念。post

特色
  1. 挂起函数能用普通函数的方式获取参数和返回值
  2. 调用挂起函数时,可能会挂起当前协程(若是挂起函数的相关调用已经有结果,那么系统可能会选择不挂起),而不会挂起所在的线程。
  3. 挂起函数执行结束后,协程会自动恢复执行,此时才能继续执行挂起函数后续的代码
  4. 挂起函数只能在协程或其余挂起函数中调用,不然会编译报错
  5. suspend能够将普通函数、扩展函数、lambda表达式均标记为挂起函数

CoroutineScope

官方描述:为协程定义了一个范围this

Defines a scope for new coroutines.spa

也能够理解为协程的上下文环境,更通俗点你能够将其看做为一个协程。.net

咱们再来看下官方源码中的定义:线程

public interface CoroutineScope {
    /** * Context of this scope. */
    public val coroutineContext: CoroutineContext
}
复制代码

经过这个代码咱们能够看到CoroutineScope初始定义中只有一个协程上下文CoroutineContext对象,因此协程的上下文对象实际上是由CoroutineContext决定的,所以将CoroutineScope看做协程更好理解。code

CoroutineContext

协程上下文,包含了协程中的一些元素,主要有JobCoroutineDispatcher

Job

协程的后台任务,它有本身的生命周期,该任务能够被取消。

Job能够有父Job,当父Job被取消时,其全部子Job也会被取消。

Job有三种状态:

  1. isActive 是否处于活动状态
  2. isCompleted 是否完成
  3. isCancelled 是否被取消

可参考下表:

State [isActive] [isCompleted] [isCancelled]
New (optional initial state) false false false
Active (default initial state) true false false
Completing (transient state) true false false
Cancelling (transient state) false false true
Cancelled (final state) false true true
Completed (final state) false true false

当建立协程开始执行并获取到Job对象后,若是想等该协程执行结束再执行其余的业务逻辑,那么能够调用Job.join()方法,该方法会等待该协程任务执行结束,该方法为挂起函数。

Deferred

它是Job的子类,与Job不一样的是它能够有返回值,而Job是没有返回值的。

经过调用Deferredawait()方法便可拿到返回值,而await()方法也是一个挂起函数,所以调用该方法时会挂起当前协程,直到拿到返回值协程从新恢复执行。

Android中协程结合Retrofit发起网络请求能够考虑使用该类获取请求结果

CoroutineDispatcher

协程调度器,它能够将协程的执行局限在指定的线程中,它有四个默认的实现:

  1. Dispatchers.Default 默认调度器,在使用launchasync等协程构造器建立协程时,若是不指定调度器则会使用此默认调度器,该调度器会让协程在JVM提供的共享线程池中执行
  2. Dispatchers.Main 主线程调度器,让协程在主线程即UI线程中执行
  3. Dispatchers.IO 让协程在IO线程(子线程)中执行,该调度器会与Dispatchers.Default调度器共享同一个线程池
  4. Dispatchers.Unconfined 该调度器不指定协程在某个线程中执行。设置了该调度器的协程会在调用者线程中启动执行直到第一个挂起点,挂起后,它将在挂起函数执行的线程中恢复,恢复的线程彻底取决于该挂起函数在哪一个线程执行。
  5. newSingleThreadContext 这是Kotlin另外提供的一个调度器,它会为协程启动一个新的线程。一个专用的线程是一种很是昂贵的资源。 在真实的应用程序中二者都必须被释放,当再也不须要的时候,使用 close 函数,或存储在一个顶级变量中使它在整个应用程序中被重用。

另外须要注意的是:协程调度器默认承袭外部协程的调度器。

GlobalScope

这是一个全局的CoroutineScope不会受任何Job约束,经过它建立的是全局协程,它会在整个应用的生命周期中运行,不能被取消

launch函数

这是一个扩展的CoroutineScope实例方法,同时也是一个很经常使用的协程构建器。

经过其默认参数会建立一个不会阻塞当前线程且会当即执行的协程,该方法会返回一个Job对象,该方法默认承袭所在的CoroutineScope对象的调度器。

val scope = CoroutineScope(Dispatchers.Main + Job())
scrope.launch {
    //协程实现
}
复制代码

上述代码经过launch建立的协程会在UI线程中执行

val scope = CoroutineScope(Dispatchers.Main + Job())
scrope.launch(Dispatchers.IO) {
    //协程实现
}
复制代码

上述代码经过launch建立的协程会在IO线程中执行

runBlocking

这是一个全局的协程构建器,能够在任何地方调用。

该构建器会建立一个阻塞当前线程的协程,因此该构建器不建议使用在协程内。

async

launch函数同样,也是CoroutineScope的扩展实例方法,它也是一个经常使用的协程构建器,不一样是它建立协程时返回的是Deferred,经过Deferred能够拿到执行结果

val a = async {
    log("I'm computing a piece of the answer")
    6
}
val b = async {
    log("I'm computing another piece of the answer")
    7
}
log("The answer is ${a.await() * b.await()}")
复制代码

delay

全局函数

  1. 让协程休眠指定时间,相似于Java中的Thread.sleep的做用
  2. delay是一个挂起函数,调用后不会阻塞挂起当前线程
  3. 当协程的休眠时间到了以后,当前所处协程会从新恢复执行

withContext

切换协程上下文,通常主要用来切换协程所在的线程环境,如从主线程切换到IO线程。

调用该方法不会建立新的协程,同时是一个挂起函数

该方法会有一个返回值,其返回值为withContext中lambda表达式的返回值