协程的创建
来看个稍微复杂点的函数
fun main(){
// 这里仅仅是创建协程
val continuation= suspend {
println("suspend thread:"+Thread.currentThread().id)
// 协程体
println("in coroutine.")
5
}.createCoroutine(object :Continuation<Int>{
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Int>) {
println("resumeWith thread:"+Thread.currentThread().id)
println("coroutine end $result")
}
})
// 这里才是 执行协程
continuation.resume(Unit)
//别让主线程直接结束了
println("main thread:"+Thread.currentThread().id)
Thread.sleep(10000)
}
复制代码
来看下执行结果:
创建协程的关键函数
看名字可以得知 createCoroutine 是创建协程的关键函数 我们来剖析一下它。
协程的启动
前面我们了解到 协程创建的过程,现在来看下协程是如何启动的
由前文可以看到 createCoroutine 函数的返回值是SafeContinudation,这个东西为啥 可以调用resume方法 来直接让协程启动呢?
我们debug 看一下:
我们重点看一下 TestKtcontinuation$1.invokeSuspend(Test.kt)
他实际上就是编译阶段生成的一个匿名内部类,这个匿名内部类有一个invokeSuspend方法,这个方法里面 其实就是我们的协程体,
讲白了 create函数 最终返回的 就是一个 大马甲,这个大马甲你看起来是什么Continuation ,其实本质上
就是我们 suspend lambda里面定义的那个 协程体
这就是为什么 我们调用resume 可以启动一个协程的原因。
我们当然可以通过start 函数 在创建一个协程以后 直接启动
// 这里仅仅是创建协程
val continuation= suspend {
println("suspend thread:"+Thread.currentThread().id)
// 协程体
println("in coroutine.")
5
}.startCoroutine(object :Continuation<Int>{
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Int>) {
println("resumeWith thread:"+Thread.currentThread().id)
println("coroutine end $result")
}
})
复制代码
函数的挂起
首先看下面的代码:
fun main(){
GlobalScope.launch {
println("GlobalScope thread start :"+Thread.currentThread().id+" time:${System.currentTimeMillis()}")
val result=suspendFunc02(1,2)
println(result)
println("GlobalScope thread end :"+Thread.currentThread().id+" time:${System.currentTimeMillis()}")
}
println("main thread:"+Thread.currentThread().id)
Thread.sleep(10000)
}
suspend fun suspendFunc02(a:Int,b:Int):Int{
return suspendCoroutine<Int> {continuation ->
println("suspendFunc02 start :"+Thread.currentThread().id+" time:${System.currentTimeMillis()}")
thread {
println("suspendFunc02 thread in :"+Thread.currentThread().id)
Thread.sleep(5000)
continuation.resumeWith(Result.success(a+b))
}
println("suspendFunc02 end :"+Thread.currentThread().id+" time:${System.currentTimeMillis()}")
}
}
复制代码
看下执行结果 能更加直观一些:
这里一定要注意 挂起函数并不一定真的会挂起,只是提供了挂起的条件。 比如我们用suspend 定义一个函数,如果这个函数里面没有 切换线程的操作,那么这个suspend 在ide中 会显示为灰色
问题来了,那到底什么时候才会被挂起?
出现异步调用才会真正的挂起,直到对应的Continutaion的resume函数被执行,调用才会恢复执行
这里要好好看下上面的 执行结果,多看几遍就能真正理解了
协程的上下文
前文我们在创建一个协程的过程中 看到了context 这个概念
来看下这个协程上下文到底是个啥呢 这个接口代码很少 我们直接上代码
public interface CoroutineContext {
public operator fun <E : Element> get(key: Key<E>): E?
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
public operator fun plus(context: CoroutineContext): CoroutineContext =
if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
context.fold(this) { acc, element ->
val removed = acc.minusKey(element.key)
if (removed === EmptyCoroutineContext) element else {
// make sure interceptor is always last in the context (and thus is fast to get when present)
val interceptor = removed[ContinuationInterceptor]
if (interceptor == null) CombinedContext(removed, element) else {
val left = removed.minusKey(ContinuationInterceptor)
if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
CombinedContext(CombinedContext(left, element), interceptor)
}
}
}
public fun minusKey(key: Key<*>): CoroutineContext
public interface Key<E : Element>
public interface Element : CoroutineContext {
public val key: Key<*>
public override operator fun <E : Element> get(key: Key<E>): E? =
@Suppress("UNCHECKED_CAST")
if (this.key == key) this as E else null
public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
public override fun minusKey(key: Key<*>): CoroutineContext =
if (this.key == key) EmptyCoroutineContext else this
}
}
复制代码
代码很简单,总结一下:
协程的上下文 本质上 就是一个集合,这个List的类型是Element, 要注意的是这个Element本身也是继承了CorotineContext 这个接口
到这里 我们应该就有一点敏感性了哈, 既然你的元素是一个element 接口,那我们看下有多少个class 继承了你这个接口 就可以知道 所谓的协程上下文 到底会有多少东西
这个技巧 我们在阅读很多源码的时候都会用到。
看下:
可以看出来 协程的上下文 中的element 子类 有很多,重点我们就关注一下 红色箭头标注的就可以了。
比如指定 协程的名称,拦截器, 等等。
我们可以尝试着创建自己的 协程Context 来加深对这个概念的理解(其实你可以把他想象为一个key value的键值对,虽然里面的存储数据结构是List 但你可以按照key-value的 形象来理解)
下面的例子我们创建了一个协程的context,指定了协程的名字 以及 异常处理器
fun main(){
var context=CoroutineName("testCoroutine")+ CoroutineExceptionHandler { coroutineContext, throwable ->
println("handler exception!!!")
}
// 这里仅仅是创建协程
suspend {
println("in coroutine [${coroutineContext[CoroutineName]}]")
throw IllegalArgumentException("do exception")
}.startCoroutine(object :Continuation<Int>{
override val context: CoroutineContext
get() = context
override fun resumeWith(result: Result<Int>) {
result.onFailure {
this.context[CoroutineExceptionHandler]?.handleException(this.context,it)
}
}
})
//别让主线程直接结束了
println("main thread:"+Thread.currentThread().id)
Thread.sleep(10000)
}
复制代码
可以看下执行结果: