参考资料
搞个例外,先推荐几篇很好的资料,不然理解会比较困难,不从基础讲起了。
Kotlin Coroutine 原理解析
Kotlin Coroutines(协程) 完全解析(二),深入理解协程的挂起、恢复与调度
深入理解 Kotlin Coroutine (一)
CoroutineContext
协程的上下文。可以通过context[CoroutineContext.Element]
拿到当前context
关联的任意element
,后面要看到的拦截器也是靠interface ContinuationInterceptor : CoroutineContext.Element
与上下文关联起来的
plus
对于context
,我们会经常看到:
Dispatchers.IO + job
@NotNull
public final CoroutineContext invoke(@NotNull CoroutineContext paramCoroutineContext, @NotNull CoroutineContext.Element paramElement)
{
Intrinsics.checkParameterIsNotNull(paramCoroutineContext, "acc");
Intrinsics.checkParameterIsNotNull(paramElement, "element");
CoroutineContext localCoroutineContext1 = paramCoroutineContext.minusKey(paramElement.getKey());
if (localCoroutineContext1 == EmptyCoroutineContext.INSTANCE) {
return (CoroutineContext)paramElement;
}
ContinuationInterceptor localContinuationInterceptor = (ContinuationInterceptor)localCoroutineContext1.get((CoroutineContext.Key)ContinuationInterceptor.Key);
CombinedContext localCombinedContext;
if (localContinuationInterceptor == null)
{
localCombinedContext = new CombinedContext(localCoroutineContext1, paramElement);
}
else
{
CoroutineContext localCoroutineContext2 = localCoroutineContext1.minusKey((CoroutineContext.Key)ContinuationInterceptor.Key);
if (localCoroutineContext2 == EmptyCoroutineContext.INSTANCE) {
localCombinedContext = new CombinedContext((CoroutineContext)paramElement, (CoroutineContext.Element)localContinuationInterceptor);
} else {
localCombinedContext = new CombinedContext((CoroutineContext)new CombinedContext(localCoroutineContext2, paramElement), (CoroutineContext.Element)localContinuationInterceptor);
}
}
return (CoroutineContext)localCombinedContext;
}
Continuation
BaseContinuationImpl
传入一个completion
作为协程结束后的操作行为。
resumeWith 精华部分
首先自己invokeSuspend(paramObject)
如果返回IntrinsicsKt.getCOROUTINE_SUSPENDED()
说明需要暂停
public final void resumeWith(@NotNull Object paramObject)
{
DebugProbesKt.probeCoroutineResumed((Continuation)this);
BaseContinuationImpl localBaseContinuationImpl1 = (BaseContinuationImpl)this;
Continuation localContinuation;
Object localObject2;
for (Object localObject1 = paramObject;; localObject1 = localObject2)
{
BaseContinuationImpl localBaseContinuationImpl2 = localBaseContinuationImpl1;
localContinuation = localBaseContinuationImpl2.completion;
if (localContinuation == null) {
Intrinsics.throwNpe();
}
try
{
Object localObject3 = localBaseContinuationImpl2.invokeSuspend(localObject1);
if (localObject3 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
return;
}
localObject2 = Result.constructor-impl(localObject3);
}
catch (Throwable localThrowable)
{
localObject2 = Result.constructor-impl(ResultKt.createFailure(localThrowable));
}
localBaseContinuationImpl2.releaseIntercepted();
if (!(localContinuation instanceof BaseContinuationImpl)) {
break;
}
localBaseContinuationImpl1 = (BaseContinuationImpl)localContinuation;
}
localContinuation.resumeWith(localObject2);
}
ContinuationImpl
在base
的基础上加上intercepted()
SafeContinuation
suspend
时会在外面套一层safeContinuation
,判断当前是继续执行还是resume
public SafeContinuation(@NotNull Continuation super T> paramContinuation)
{
this(paramContinuation, CoroutineSingletons.UNDECIDED);
}
resumeWith
public void resumeWith(@NotNull Object paramObject)
{
for (;;)
{
Object localObject = this.result;
if (localObject == CoroutineSingletons.UNDECIDED)
{
if (!RESULT.compareAndSet(this, CoroutineSingletons.UNDECIDED, paramObject)) {}
}
else
{
if (localObject != IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
break;
}
if (RESULT.compareAndSet(this, IntrinsicsKt.getCOROUTINE_SUSPENDED(), CoroutineSingletons.RESUMED))
{
this.delegate.resumeWith(paramObject);
return;
}
}
}
throw ((Throwable)new IllegalStateException("Already resumed"));
}
如果当前continuation
要继续时,状态可以从:
- CoroutineSingletons.UNDECIDED -> paramObject
如果之前协程处于suspend
状态,那就转成CoroutineSingletons.RESUMED
状态,同时this.delegate.resumeWith(paramObject)
getOrThow
@Nullable
public final Object getOrThrow()
{
Object localObject = this.result;
if (localObject == CoroutineSingletons.UNDECIDED)
{
if (RESULT.compareAndSet(this, CoroutineSingletons.UNDECIDED, IntrinsicsKt.getCOROUTINE_SUSPENDED())) {
return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
localObject = this.result;
}
if (localObject == CoroutineSingletons.RESUMED) {
return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
if ((localObject instanceof Result.Failure)) {
throw ((Result.Failure)localObject).exception;
}
return localObject;
}
如果想要拿到结果时,当前result仍为CoroutineSingletons.UNDECIDED
,那就说明当前协程需要暂停,状态转为IntrinsicsKt.getCOROUTINE_SUSPENDED()
CombinedContext
经常会看到:
context get() = job() + IO....
这里+
号就重载成了CombinedContext
IntrinsicsKt__IntrinsicsJvmKt
createCoroutineUnintercepted
直接看简单点的吧:
if ((paramFunction2 instanceof BaseContinuationImpl)) {
return ((BaseContinuationImpl)paramFunction2).create(paramR, localContinuation);
}
private static final Object startCoroutineUninterceptedOrReturn(@NotNull Function1 super Continuation super T>, ? extends Object> paramFunction1, Continuation super T> paramContinuation)
{
if (paramFunction1 == null) {
throw new TypeCastException("null cannot be cast to non-null type (kotlin.coroutines.Continuation) -> kotlin.Any?");
}
return ((Function1)TypeIntrinsics.beforeCheckcastToFunctionOfArity(paramFunction1, 1)).invoke(paramContinuation);
}
其实就是function.invoke()..
具体例子
fun main(args: Array) {
log("before coroutine")
//启动我们的协程
asyncCalcMd5("test.zip") {
log("in coroutine. Before suspend.")
//暂停我们的线程,并开始执行一段耗时操作
val result: String = suspendCoroutine {
continuation ->
log("in suspend block.")
continuation.resume(calcMd5(continuation.context[FilePath]!!.path))
log("after resume.")
}
log("in coroutine. After suspend. result = $result")
}
log("after coroutine")
}
/**
* 上下文,用来存放我们需要的信息,可以灵活的自定义
*/
class FilePath(val path: String): AbstractCoroutineContextElement(FilePath){
companion object Key : CoroutineContext.Key
}
fun asyncCalcMd5(path: String, block: suspend () -> Unit) {
val continuation = object : Continuation {
override fun resumeWith(result: Result) {
log("resume: $result")
}
override val context: CoroutineContext
get() = FilePath(path)
}
block.startCoroutine(continuation)
}
fun calcMd5(path: String): String{
log("calc md5 for $path.")
//暂时用这个模拟耗时
Thread.sleep(1000)
//假设这就是我们计算得到的 MD5 值
return System.currentTimeMillis().toString()
}
这里block
被转成:
传入的continuation
即为block.startCoroutine(continuation)
,startCoroutine
会被编译器转化为startCoroutine(Function, Continutation)
,这里
Function: block
Continuation: val continutaion
startCoroutine
@SinceKotlin(version="1.3")
public static final void startCoroutine(@NotNull Function2 super R, ? super Continuation super T>, ? extends Object> paramFunction2, R paramR, @NotNull Continuation super T> paramContinuation)
{
Intrinsics.checkParameterIsNotNull(paramFunction2, "receiver$0");
Intrinsics.checkParameterIsNotNull(paramContinuation, "completion");
Continuation localContinuation = IntrinsicsKt.intercepted(IntrinsicsKt.createCoroutineUnintercepted(paramFunction2, paramR, paramContinuation));
Unit localUnit = Unit.INSTANCE;
localContinuation.resumeWith(Result.constructor-impl(localUnit));
}
先创建,再resume
创建分两步:
IntrinsicsKt.createCoroutineUnintercepted(paramFunction2, paramR, paramContinuation)
if ((paramFunction1 instanceof BaseContinuationImpl)) {
return ((BaseContinuationImpl)paramFunction1).create(localContinuation);
}
IntrinsicsKt.intercepted
那我们俩看block
转换成了什么样子的paramFunction
:
final class block extends SuspendLambda
implements Function1, Object>{
@NotNull
public final Continuation create(@NotNull Continuation> paramContinuation)
{
//create出baseCoroutineImpl
Intrinsics.checkParameterIsNotNull(paramContinuation, "completion");
return new 1(this.this$0, paramContinuation);
}
public final Object invoke(Object paramObject)
{
return ((1)create((Continuation)paramObject)).invokeSuspend(Unit.INSTANCE);
}
//resumeWith以后,先走invokeSuspend
@Nullable
public final Object invokeSuspend(@NotNull Object paramObject)
{
Object localObject1 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object localObject2;
switch (this.label)
{
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
case 1:
((1)this.L$0);
if (!(paramObject instanceof Result.Failure)) {
localObject2 = paramObject;
} else {
throw ((Result.Failure)paramObject).exception;
}
break;
case 0:
if ((paramObject instanceof Result.Failure)) {
break label276;
}
this.this$0.log("in coroutine. Before suspend.");
this.L$0 = this;
this.label = 1;
SafeContinuation localSafeContinuation = new SafeContinuation(IntrinsicsKt.intercepted(this));
Continuation localContinuation = (Continuation)localSafeContinuation;
this.this$0.log("in suspend block.");
HongMoActivity localHongMoActivity1 = this.this$0;
CoroutineContext.Element localElement = localContinuation.getContext().get((CoroutineContext.Key)HongMoActivity.FilePath.Key);
if (localElement == null) {
Intrinsics.throwNpe();
}
String str1 = localHongMoActivity1.calcMd5(((HongMoActivity.FilePath)localElement).getPath());
localContinuation.resumeWith(Result.constructor-impl(str1));
this.this$0.log("after resume.");
localObject2 = localSafeContinuation.getOrThrow();
if (localObject2 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended(this);
}
if (localObject2 == localObject1) {
return localObject1;
}
break;
}
String str2 = (String)localObject2;
HongMoActivity localHongMoActivity2 = this.this$0;
StringBuilder localStringBuilder = new StringBuilder();
localStringBuilder.append("in coroutine. After suspend. result = ");
localStringBuilder.append(str2);
localHongMoActivity2.log(localStringBuilder.toString());
return Unit.INSTANCE;
label276:
throw ((Result.Failure)paramObject).exception;
}
}
转成safeContinuation
后,计算得到结果resumeWith+getOrThrow
然后传递给completion
。
关键代码在于:
public suspend inline fun suspendCoroutine(crossinline block: (Continuation) -> Unit): T =
suspendCoroutineUninterceptedOrReturn { c: Continuation ->
val safe = SafeContinuation(c.intercepted())
block(safe)
safe.getOrThrow()
}
这里会转换成cps
代码,
- 使用
suspendCoroutine
时,先转成SafeContinuation
, -
getOrThrow
如果拿到的是suspend
,直接返回。 - 返回到前面
BaseContinuationImpl
的循环后,因为completion
不是BaseContinuationImpl
,跳出,走到competion.resumeWith
结束。
异步时
case 0:
if ((paramObject instanceof Result.Failure)) {
break label228;
}
this.this$0.log("in coroutine. Before suspend.");
this.L$0 = this;
this.label = 1;
SafeContinuation localSafeContinuation = new SafeContinuation(IntrinsicsKt.intercepted(this));
Continuation localContinuation = (Continuation)localSafeContinuation;
this.this$0.log("in suspend block.");
HongMoActivity.access$getExecutor$p(this.this$0).submit((Runnable)new HongMoActivity.main.1.invokeSuspend..inlined.suspendCoroutine.lambda.1(localContinuation, this));
localObject2 = localSafeContinuation.getOrThrow();
if (localObject2 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended(this);
}
if (localObject2 == localObject1) {
return localObject1;
}
break;
}
可以看到submit
后,直接getOrThrow
此时拿到的为suspend
所以直接返回,
在runnable
中:
public final void run()
{
Continuation localContinuation = this.$continuation;
HongMoActivity localHongMoActivity = this.this$0.this$0;
CoroutineContext.Element localElement = this.$continuation.getContext().get((CoroutineContext.Key)HongMoActivity.FilePath.Key);
if (localElement == null) {
Intrinsics.throwNpe();
}
String str = localHongMoActivity.calcMd5(((HongMoActivity.FilePath)localElement).getPath());
localContinuation.resumeWith(Result.constructor-impl(str));
this.this$0.this$0.log("after resume.");
}
safeContinunation
重新resumeWith
会进入label = 1
返回值给completion
总结
suspend
代表挂起,即线程执行到这里时可能会直接break
返回,同时会增加一个continuation
代表一个继续点,这个也好理解,下次被resume
时就会从这个continuation
继续执行。
待续
-
async|launch
等是如何精简协程操作的 -
intercept
是怎么操作的 -
combineContext
到底有什么意义 -
异步
是如何拿到continuation
且继续的 -
suspendCoroutine
用什么进行替代
下期继续学习上面这些疑惑,btw新年快乐啊,这篇拖了太久了打脸