CoroutineScope的一个用法

在执行下面的composable 函数里代码的时候发现挂起函数 playerOne.run(), playerOne.run()几乎没怎么执行

复制代码
    var raceInProgress by remember { mutableStateOf(true) }

    if (raceInProgress) {
        LaunchedEffect(playerOne, playerTwo) {

            launch() { playerOne.run() }
            launch { playerTwo.run() }

            raceInProgress = false
        }
    }

    suspend fun run() {
        try {
            Log.d("test", "run starts")
            while (currentProgress < maxProgress) {
                Log.d("test", "run: $currentProgress")
                delay(progressDelayMillis)
                currentProgress += progressIncrement
            }
        }catch (e: CancellationException) {
            Log.e("RaceParticipant", "$name: ${e.message}")
            throw e // Always re-throw CancellationException.
        }
    }

日志如下:

复制代码
2026-01-05 23:22:14.761  5941-5941  test                    com.example.racetracker              D  run starts
2026-01-05 23:22:14.761  5941-5941  test                    com.example.racetracker              D  run: 0
2026-01-05 23:22:14.761  5941-5941  test                    com.example.racetracker              D  run starts
2026-01-05 23:22:14.761  5941-5941  test                    com.example.racetracker              D  run: 0
2026-01-05 23:22:14.793  5941-5941  RaceParticipant         com.example.racetracker              E  Player 1: The coroutine scope left the composition
2026-01-05 23:22:14.793  5941-5941  RaceParticipant         com.example.racetracker              E  Player 2: The coroutine scope left the composition

可以看到,coroutine 被取消了。

原因是,launch 函数是非阻塞的, 所以 两个 launch函数把协程函数 run() 起来后不等 run()执行完即返回,然后把raceInProgress设置为false,触发重组,导致 launchedEffect被移出,从而 协程函数run()被取消。

解决方法是把 run函数加入到 coroutineScope里.这个 coroutineScope是阻塞函数,等到run()函数执行完才返回。

复制代码
    var raceInProgress by remember { mutableStateOf(false) }

    if (raceInProgress) {
        LaunchedEffect(playerOne, playerTwo) {
            coroutineScope {
                launch() { playerOne.run() }
                launch { playerTwo.run() }
            }
            raceInProgress = false
        }
    }

coroutineScope的声明如下:

复制代码
package kotlinx.coroutines

public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn { uCont ->
        val coroutine = ScopeCoroutine(uCont.context, uCont)
        coroutine.startUndispatchedOrReturn(coroutine, block)
    }
}

参考:

https://developer.android.google.cn/codelabs/basic-android-kotlin-compose-coroutines-android-studio?hl=en#5

相关推荐
环境栈笔记12 分钟前
指纹浏览器安全评测方法:核对环境、数据与权限后再选型
前端·人工智能·后端·自动化
星核0penstarry15 分钟前
DeepSeek-V4-Flash 正式公测:大模型行业进入「极速平价普惠时代」
java·开发语言·人工智能
打妖妖灵滴哪吒16 分钟前
前端开发/UI设计师-APP中如何进行可发现性设计
前端·ui
Kinghiee20 分钟前
从使用场景切入前端常见设计模式
前端·设计模式
xieliyu.43 分钟前
Java 线程池入门详解:线程池基础定义 + ThreadPoolExecutor 全参构造方法逐参数解析
java·开发语言·idea·javaee
老白干3 小时前
jjwt 0.9.1 在 JDK 11+ 上的两个“坑”与完整解决方案
java·python·log4j
圆山猫9 小时前
[Virtualization](四):Linux KVM/RISC-V 的 vCPU 运行路径
java·linux·risc-v
城管不管10 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
郭老二10 小时前
【前端】vue3:编译步骤
前端
IT小白杨10 小时前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器