Kotlin - 协程基础及原理,Android面试题及答案

本文深入探讨了Kotlin协程的基础,包括suspend关键字的原理、协程的创建与挂起过程。通过源码分析,解释了协程接口、createCoroutineUnintercepted、intercepted等关键函数的作用。此外,还介绍了协程作用域、GlobalScope.launch的源码分析以及自定义协程上下文的方法。通过对协程的全面理解,有助于在Android面试中更好地解答相关问题。
摘要由CSDN通过智能技术生成

GlobalScope.launch {

semaphore.withPermit {

counter ++

}

}

}

Thread.sleep(500) //暂停一会儿等待所有协程执行结束

println(“The final count is $counter”)

}

注意:只有在 permits = 1 时才和 Mutex 功能相同。

源码分析

suspend

我们来看 suspend 修饰函数和修饰 lambda 的区别。

挂起函数:

suspend fun suspendFun() {

}

编译成 java 代码如下:

@Nullable

public final Object suspendFun(@NotNull Continuation $completion) {

return Unit.INSTANCE;

}

可以看到挂起函数其实隐藏着一个 Continuation 协程实例参数,而这个参数其实就来源于协程体或者其他挂起函数,因此挂起函数只能在协程体内或其他函数内调用了。

suspend 修饰 lambda 表达式:

suspend {}

// 反编译结果如下

Function1 var2 = (Function1)(new Function1((Continuation)null) {

int label;

@Nullable

public final Object invokeSuspend(@NotNull Object $result) {

switch(this.label) {

case 0:

return Unit.INSTANCE;

default:

}

}

@NotNull

public final Continuation create(@NotNull Continuation completion) {

Function1 var2 = new (completion);

return var2;

}

public final Object invoke(Object var1) {

return (()this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);

}

});

suspend lambda 实际会被编译成 SuspendLambda 的子类。suspendLambda 的继承关系如下图:

image

通过反编译的代码可以发现我们在协程体内编写的代码最终是在 invokeSuspend 函数内执行的。而在 BaseContinuationImpl 内实现了 Continuation 协程接口的 resumeWidth 函数,并在其内调用了 invokeSuspend 函数。

suspend 关键字的介绍先到这里,接下来我们看协程是如何创建并运行的。

协程是如何被创建的

文件地址 kotlin.coroutines.Continuation.kt

Continuation.kt 文件基本属于协程的基础核心了,搞懂了它也就相当于搞懂了协程的基础原理。

  • 协程接口的定义;

  • 唤醒或启动协程的函数;

  • 四种创建协程的函数;

  • 帮助获取协程内的协程实例对象的函数。

首先是协程的接口声明,非常简单:

/**

  • 协程接口,T 表示在最后一个挂起点恢复时的返回值类型

*/

public interface Continuation {

/**

  • 协程上下文

*/

public val context: CoroutineContext

/**

  • 这个函数的功能有很多,它可以启动协程,也可以恢复挂点,还可以作为最后一次挂起点恢复时输出协程的结果

*/

public fun resumeWith(result: Result)

}

协程接口声明之后 Continuation.kt 文件提供了两个调用 resumeWith 函数的函数:

public inline fun Continuation.resume(value: T): Unit =

resumeWith(Result.success(value))

public inline fun Continuation.resumeWithException(exception: Throwable): Unit =

resumeWith(Result.failure(exception))

这两个函数除了传参一成功一失败,它们的功能是一模一样的,都是直接调用了 resumeWith 函数。相当于是 resumeWith 函数的封装。

再然后就是四种创建协程的方式了:

public fun (suspend () -> T).createCoroutine(

completion: Continuation

): Continuation =

SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)

public fun <R, T> (suspend R.() -> T).createCoroutine(

receiver: R,

completion: Continuation

): Continuation =

SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)

public fun (suspend () -> T).startCoroutine(

completion: Continuation

) {

createCoroutineUnintercepted(completion).intercepted().resume(Unit)

}

public fun <R, T> (suspend R.() -> T).startCoroutine(

receiver: R,

completion: Continuation

) {

createCoroutineUnintercepted(receiver, completion).intercepted().resume(Unit)

}

这四种方式可以说是相似度超高,createCoroutinestartCoroutine 最大的区别在于,通过 createCoroutine 创建的协程需要掉用 resume 函数启动,而 startCoroutine 函数内部已经默认调用了 resume 函数。那我们先用第一种方式创建一个协程:

// 创建协程

val continuation = suspend {

println(“In Coroutine”)

}.createCoroutine(object : Continuation {

override fun resumeWith(result: Result) {

println(result)

}

override val context = EmptyCoroutineContext

})

// 启动协程

continuation.resume(Unit)

调用 createCoroutine 函数创建协程时传入了 Continuation 协程的匿名类对象,诶?好像有点不对,为什么创建协程的时候要传一个协程实例进去,直接用不就成了。想知道为什么的话,那就需要看看 createCoroutine 到底做了什么操作了。

SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)

首先调用的是 createCoroutineUnintercepted 函数,它的源码可以在 kotlin.coroutines.intrinsics.IntrinsicsJvm.kt 内找到:

public actual fun (suspend () -> T).createCoroutineUnintercepted(

completion: Continuation

): Continuation {

val probeCompletion = probeCoroutineCreated(completion)

return if (thi

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值