Kotlin协程知识点说明

本文主要讲解与 Android 中 Kotlin 协程的知识点。

挂起点和续体

在理解 Kotlin 协程的相关知识点前,我们需要先明确一些概念:在 Android 中,Kotlin 的协程框架其实是基于 java 线程和线程池的二次封装。传统的 java 异步写法是基于回调的,随着业务代码的膨胀,回调会越来越多,层级会越来越深,最终导致 “回调地狱”。为了解决这个问题,Kotlin 协程对 java 线程和线程池做了二次封装,结合上 Kotlin 编译器的处理,最终实现了不用回调就能写出正确的异步代码,这种机制就是协程的挂起与恢复。

协程的挂起与恢复依赖于 挂起点 和 续体,挂起点用 suspend 关键字标识,告诉编译器这段代码会异步执行,而续体则是协程恢复之后要执行的剩余代码,用 Continuation 类标识。Continuation 类的定义如下:

public interface Continuation<in T> {
    public val context: CoroutineContext
    public fun resumeWith(result: Result<T>)
}

Continuation 类的定义非常简单,仅包含 context 变量和 resumeWith 方法。通俗的讲,Continuation 就是一个异步执行后的回调。resumeWith 就是其回调时调用的方法。

传统的 java 异步代码,我们会在完成异步操作后,将结果通过回调返回:

Callback callback = new Callback() {
    @Override
    public void onSuccess(data: Data) {
        // 异步执行结束后,打印结果
        System.out.println("sucess: data is " + data);
    }
};

public void doAsync(callback: Callback) {
    new Thread() {
        // 执行异步代码
        callback.onSuccess(data);
    }.run();
}

采用协程的写法如下,doAsync 这个 suspend 方法被调用时,就是挂起点被挂起了,后续的打印操作,就是续体:

GlobalScope.launch {
    val data = doAsync()
    println("sucess: data is $data")
}

suspend fun doAsync(): Data {
    // 执行异步代码
    val data = doSomething()
    return data
}

类比到协程里的 Continuation 类,我们就可以做这样的转换:

Continuation continuation = new Continuation() {
    @Override
    public void resumeWith(data: Data) {
        // 异步执行结束后,打印结果
        System.out.println("sucess: data is " + data);
    }
};

public void doAsync(callback: Callback) {
    new Thread() {
        // 执行异步代码
        continuation.resumeWith(data);
    }.run();
}

从上可以看出,我们可以简单的将 Continuation 视作一个异步回调。

协程作业

Job 表示协程作业,其代表着协程,是协程的句柄。使用 launch 或者 async 方法创建的每个协程都会返回一个 Job 对象,该 Job 实例是协程的唯一标识并管理其运行状态。每一个 Job 对象都有着以下的运行状态:

协程作用域

CoroutineScope 是协程生效的范围,可以理解成协程的作用域和生命周期,其描述了协程可用的时机。开启 Kotlin 协程需要在 CoroutineScope 上调用 launch 或者 async 方法。