协程是通过编译技术实现(不需要虚拟机VM/操作系统OS的支持),通过插入相关代码来生效! 与之相反,线程/进程是需要虚拟机VM/操作系统OS的支持,通过调度CPU执行生效!
Kotlin Coroutines(协程) 完全解析(一),协程简介
Kotlin Coroutines(协程) 完全解析(二),深入理解协程的挂起、恢复与调度
Kotlin Coroutines(协程) 完全解析(三),封装异步回调、协程间关系及协程的取消
Kotlin Coroutines(协程) 完全解析(四),协程的异常处理
Kotlin Coroutines(协程) 完全解析(五),协程的并发
英文:
https://kotlinlang.org/docs/reference/coroutines/basics.html
https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutines-guide.md
https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md
中文:
https://www.kotlincn.net/docs/reference/coroutines/coroutines-guide.html
https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope
https://developer.android.google.cn/kotlin/coroutines
异步的含义是被调用的方法执行完之后,无法直接拿到返回值(切换了线程),需要通过回调接收返回值
协程相当于提前切换到子线程,然后同步走逻辑,进而改变每一层的异步调用为同步,
协程将同步方法和线程切换两者相隔离,所有方法都是同步方法,单独控制执行线程,避免异步方法去接收回调参数
区别:
fun requestTokenAsync(cb: (Token) -> Unit) { ... }
fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
    requestTokenAsync { token ->
        createPostAsync(token, item) { post ->
            processPost(post)
        }
    }
}
fun requestTokenAsync(): CompletableFuture<Token> { ... }
fun createPostAsync(token: Token, item: Item): CompletableFuture<Post> { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
    requestTokenAsync()
            .thenCompose { token -> createPostAsync(token, item) }
            .thenAccept { post -> processPost(post) }
            .exceptionally { e ->
                e.printStackTrace()
                null
            }
}
fun requestToken(): Token { ... }
fun createPost(token: Token, item: Item): Post { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
    Single.fromCallable { requestToken() }
            .map { token -> createPost(token, item) }
            .subscribe(
                    { post -> processPost(post) }, // onSuccess
                    { e -> e.printStackTrace() } // onError
            )
}
suspend fun requestToken(): Token { ... }   // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... }  // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
    GlobalScope.launch {
        val token = requestToken()
        val post = createPost(token, item)
        processPost(post)
        // 需要异常处理,直接加上 try/catch 语句即可
    }
}
协程的挂起通过suspend挂起函数实现,协程的恢复通过Continuation.resumeWith实现。
suspend fun requestToken(): Token { ... }
实际上在 JVM 中更像下面这样:
Object requestToken(Continuation<Token> cont) { ... }
Continuation的定义如下,类似于一个通用的回调接口:
/**
 * Interface representing a continuation after a suspension point that returns value of type `T`.
 */
public interface Continuation<in T> {
    /**
     * Context of the coroutine that corresponds to this continuation.
     */
    public val context: CoroutineContext
    /**
     * Resumes the execution of the corresponding coroutine passing successful or failed [result] as the
     * return value of the last suspension point.
     */
    public fun resumeWith(result: Result<T>)
}
现在再看之前postItem函数:
suspend fun requestToken(): Token { ... }   // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... }  // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
    GlobalScope.launch {
        val token = requestToken()
        val post = createPost(token, item)
        processPost(post)
    }
}
然而,协程内部实现不是使用普通回调的形式,而是使用状态机来处理不同的挂起点,大致的 CPS(Continuation Passing Style) 代码为:
// 编译后生成的内部类大致如下
final class postItem$1 extends SuspendLambda ... {
    public final Object invokeSuspend(Object result) {
        ...
        switch (this.label) {
            case 0:
                this.label = 1;
                token = requestToken(this)
                break;
            case 1:
                this.label = 2;
                Token token = result;
                post = createPost(token, this.item, this)
                break;
            case 2:
                Post post = result;
                processPost(post)
                break;
        }
    }
}
上面代码中每一个挂起点和初始挂起点对应的 Continuation 都会转化为一种状态,协程恢复只是跳转到下一种状态中。挂起函数将执行过程分为多个 Continuation 片段,并且利用状态机的方式保证各个片段是顺序执行的。
cancel()或者异常结束,会立即取消它的所有子协程