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("Thread: ${Thread.currentThread().name}")
}
输出的结果还是在主线程。
通过 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)