Kotlin协程

Leon
2022-11-04 / 0 评论 / 27 阅读 / 正在检测是否收录...

Kotlin协程

就像 Java 中的 Executor 和 Android 中的 AsyncTask,Kotlin 中的协程也有对 Thread API 的封装,让我们可以在写代码时,不用关注多线程就能够很方便地写出并发操作。

Kotlin协程是由官方提供的一套线程API,使用看起来同步方式写异步代码——非堵塞式挂起。

消除回调

launch({
    val user = api.getUser() // 网络请求(IO 线程)
    nameTv.text = user.name  // 更新 UI(主线程)
})



api.getUser()
    .enqueue(object : Callback<User> {

        override fun onResponse(call : Call<User>,
                               response:Response<User>){
            runOnUiThread {
                nameTv.text = response.body()?.name
            }
        }
    })

suspend 是 Kotlin 协程最核心的关键字,代码执行到 suspend 函数的时候会挂起,并且这个挂起是非阻塞式的,它不会阻塞你当前的线程。

launch ,async 或者其他函数创建的协程,在执行到某一个 suspend 函数的时候,这个协程会被suspend,也就是被挂起。

GlobalScope.launch(Dispatchers.Main) {
  val image = suspendingGetImage(imageId)  // 获取图片
  avatarIv.setImageBitmap(image)           // 显示出来
}

suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO) {
  ...
}


suspend fun suspendUntilDone() {
  while (!done) {
    delay(5)
  }
}

但是

suspend fun suspendingPrint() {
  println(&quot;Thread: ${Thread.currentThread().name}&quot;)
}

输出的结果还是在主线程。

通过 withContext 源码可以知道,它本身就是一个挂起函数,它接收一个 Dispatcher 参数,依赖这个 Dispatcher 参数的指示,协程被挂起,然后切到别的线程。

public suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
        // 计算新上下文
        val oldContext = uCont.context
        val newContext = oldContext + context
        // 始终检查新上下文的取消
        newContext.ensureActive()
        // FAST PATH #1 -- 新上下文与旧上下文相同
        if (newContext === oldContext) {
            val coroutine = ScopeCoroutine(newContext, uCont)
            return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
        }
        // FAST PATH #2 -- 新dispatcher与旧dispatcher相同(发生了变化)
        // `equals` 被设计使用
        if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
            val coroutine = UndispatchedCoroutine(newContext, uCont)
            // There are changes in the context, so this thread needs to be updated
            withCoroutineContext(newContext, null) {
                return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
            }
        }
        // SLOW PATH -- 使用新的dispatcher
        val coroutine = DispatchedCoroutine(newContext, uCont)
        block.startCoroutineCancellable(coroutine, coroutine)
        coroutine.getResult()
    }
}

所以这个 suspend,其实并不是起到把任何协程挂起,或者说切换线程的作用。

真正挂起协程这件事,是 Kotlin 的协程框架帮我们做的。

所以我们想要自己写一个挂起函数,仅仅只加上 suspend 关键字是不行的,还需要函数内部直接或间接地调用到 Kotlin 协程框架自带的 suspend 函数才行。

在 Kotlin 里,协程就是基于线程来实现的一种更上层的工具 API,类似于 Java 自带的 Executor 系列 API 或者 Android 的 Handler 系列 API。

协程它不仅提供了方便的 API,在设计思想上是一个基于线程的上层框架

协程就是切线程。

挂起就是可以自动切回来的切线程。

挂起的非阻塞式指的是它能用看起来阻塞的代码写出非阻塞的操作。

0

评论 (0)

取消