網站首頁 編程語言 正文
Kotlin 協程的基礎元素:Continuation、SafeContinuation、CoroutineContext、CombinedContext、CancellationException、intrinsics。CombinedContext是 CoroutineContext 的一個實現類,SafeContinuation是 Continuation 的實現類。
Continuation 是什么?
class Call<T>(callBack: Call.CallBack<T>) {
fun cancel() {
}
interface CallBack<T> {
fun onSuccess(data: T)
fun onFail(throwable: Throwable)
}
}
suspend fun <T : Any> Call<T>.await(): T =
suspendCancellableCoroutine<T> { continuation ->
val call = Call(object : Call.CallBack<T> {
override fun onSuccess(data: T) {
continuation.resume(data, onCancellation = null)
}
override fun onFail(throwable: Throwable) {
continuation.resumeWithException(throwable)
}
})
continuation.invokeOnCancellation {
call.cancel()
}
}
要實現掛起函數的時候,可以使用 suspendCoroutine{}、suspendCancellableCoroutine{}這兩個高階函數,在它們的 Lambda 當中,我們可以使用它暴露出來的 continuation 對象,把程序的執行結果或異常傳到外部去。常用于實現掛起函數內部邏輯。
fun checkLength() {
runBlocking {
val result = getStrLengthSuspend("Kotlin")
println(result)
}
}
suspend fun getStrLengthSuspend(text:String):Int = suspendCoroutine {
continuation ->
thread {
Thread.sleep(1000L)
continuation.resume(text.length)
}
}
Log
6
Process finished with exit code 0
使用 suspendCoroutine{}實現了掛起函數,在它內部,使用 continuation.resume() 的方式,傳出了掛起函數的返回值。
為什么以 continuation.resume() 這樣異步的方式傳出結果,掛起函數就能接收到結果呢?
suspend fun getStrLengthSuspend(text: String): Int = suspendCoroutine { continuation ->
thread {
Thread.sleep(1000L)
continuation.resume(text.length)
}
}
fun checkLength2() {
val func = ::getStrLengthSuspend as (String, Continuation<Int>) -> Any?
func("Kotlin", object :Continuation<Int> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Int>) {
println(result.getOrNull())
}
})
Thread.sleep(2000L)
}
Log
6
Process finished with exit code 0
把函數強轉成了帶有 Continuation 的函數類型,然后通過匿名內部類的方式,創建了一個 Continuation 對象傳了進去。掛起函數的本質,就是 Callback!
把 Continuation 改為 Callback:
fun getStrLength() {
func2("Kotlin", object : MyCallBack<Int> {
override fun resume(value: Int) {
println(value)
}
})
}
fun func2(text: String, callBack: MyCallBack<Int>) {
thread {
Thread.sleep(1000L)
callBack.resume(text.length)
}
}
interface MyCallBack<T> {
fun resume(value: T)
}
Log
6
Process finished with exit code 0
Continuation 改成 Callback 以后,使用匿名內部類創建 Callback 用于接收異步結果;異步函數內部,使用 callback.resume() 將結果傳出去。
Kotlin 協程當中的 Continuation,作用其實就相當于 Callback,它既可以用于實現掛起函數,往掛起函數的外部傳遞結果;也可以用于調用掛起函數,我們可以創建 Continuation 的匿名內部類,來接收掛起函數傳遞出來的結果。
Java 代碼中如何調用 Kotlin 的掛起函數嗎?
public static void main(String[] args) {
SuspendFromJavaDo.INSTANCE.getUserInfo(new Continuation<String>() {
@NonNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NonNull Object o) {
System.out.println(o + "");
}
});
}
object SuspendFromJavaDo {
suspend fun getUserInfo():String {
delay(1000L)
return "Kotlin"
}
}
所以,實現掛起函數邏輯的時候,常用到 suspendCoroutine{}、suspendCancellableCoroutine{}。
Continuation.kt源碼
public interface Continuation<in T> {
/**
* The context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
val safe = SafeContinuation(c.intercepted())
block(safe)
safe.getOrThrow()
}
}
suspendCoroutineUninterceptedOrReturn{}實現了suspendCoroutine{}。
val safe = SafeContinuation(c.intercepted()) 包裹Continuation。
block(safe)調用 Lambda 當中的邏輯。
safe.getOrThrow() 取出 block(safe) 的運行結果,Continuation 當中是可以存儲 result 的。這個 Result 可能是正確的結果,也可能是異常。
@Suppress("UNUSED_PARAMETER", "RedundantSuspendModifier")
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
}
為什么這個高階函數的源代碼會是拋出異常呢?
“Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic.”suspendCoroutineUninterceptedOrReturn 是一個編譯器內建函數,它是由 Kotlin 編譯器來實現的。
suspendCoroutineUninterceptedOrReturn 這個高階函數的參數,它會接收一個 Lambda,類型是(Continuation<T>) -> Any?,這里的“Any?”類型,其實就能代表當前這個掛起函數是否真正掛起。
fun test() {
runBlocking {
val result = testNoSuspendCoroutinue()
println(result)
}
}
private suspend fun testNoSuspendCoroutinue() = suspendCoroutineUninterceptedOrReturn<String>{
continuation ->
return@suspendCoroutineUninterceptedOrReturn "Kotlin"
}
Log
Kotlin
Process finished with exit code 0
直接使用 suspendCoroutineUninterceptedOrReturn 實現了掛起函數,并且,在它的 Lambda 當中,我們并沒有調用 continuation.resume()。在掛起函數的外部確實也可以接收到這個結果。
private static final Object testNoSuspendCoroutinue(Continuation $completion) {
int var2 = false;
if ("Kotlin" == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended($completion);
}
return "Kotlin";
}
這個函數其實就是一個偽掛起函數,它的內部并不會真正掛起。這樣,當我們從外部調用這個函數的時候,這個函數會立即返回結果。
private suspend fun testSuspendCoroutinues() =
suspendCoroutineUninterceptedOrReturn<String> { continuation ->
thread {
Thread.sleep(2000L)
continuation.resume("Kotlin")
}
return@suspendCoroutineUninterceptedOrReturn kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
fun main() {
runBlocking {
val testSuspendCoroutinues = testSuspendCoroutinues()
println(testSuspendCoroutinues)
}
}
Kotlin
Process finished with exit code 0
使用了 continuation.resume(),掛起函數的外部也能接收到這個結果。
private static final Object testSuspendCoroutinues(Continuation $completion) {
int var2 = false;
ThreadsKt.thread$default(false, false, (ClassLoader)null, (String)null, 0, (Function0)(new TestCoroutine777Kt$testSuspendCoroutinues$2$1($completion)), 31, (Object)null);
Object var10000 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
//將 var10000 賦值為 COROUTINE_SUSPENDED 這個掛起標志位。
if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended($completion);
}
//返回掛起標志位,代表 testSuspendCoroutinues() 這個函數會真正掛起。
return var10000;
}
BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
var10000 = TestCoroutine777Kt.testSuspendCoroutinues(this);
if (var10000 == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String testSuspendCoroutinues = (String)var10000;
System.out.println(testSuspendCoroutinues);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
}), 1, (Object)null);
由于 suspend 修飾的函數,既可能返回 CoroutineSingletons.COROUTINE_SUSPENDED,也可能返回實際結果,甚至可能返回 null,為了適配所有的可能性,CPS 轉換后的函數返回值類型就只能是 Any? 了。
suspendCoroutineUninterceptedOrReturn{}這個高階函數的作用了:它可以將掛起函數當中的 Continuation 以參數的形式暴露出來,在它的 Lambda 當中,我們可以直接返回結果,這時候它就是一個“偽掛起函數”;或者,我們也可以返回 COROUTINE_SUSPENDED 這個掛起標志位,然后使用 continuation.resume() 傳遞結果。相應的,suspendCoroutine{}、suspendCancellableCoroutine{}這兩個高階函數,只是對它的一種封裝而已。
原文鏈接:https://blog.csdn.net/zhangying1994/article/details/127820334
相關推薦
- 2022-05-15 C++11:搞清楚萬能引用和右值引用
- 2023-03-03 PostgreSQL時間日期的語法及注意事項_PostgreSQL
- 2021-12-05 Go語言配置數據庫連接池的實現_Golang
- 2022-07-29 Pytest框架?conftest.py文件的使用詳解_python
- 2022-03-19 詳解C語言結構體的定義和使用_C 語言
- 2022-07-03 C語言詳解strcmp函數的分析及實現_C 語言
- 2022-07-13 CMD使用技巧和常用固定語句
- 2022-06-02 Apache?Pulsar結合Hudi構建Lakehouse方案分析_服務器其它
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支