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

相关推荐
AI人工智能+电脑小能手21 小时前
【大白话说Java面试题】【Java基础篇】第25题:JDK1.8的新特性有哪些
java·开发语言·后端·面试
Daybreak21 小时前
Mobile 端 AI 请求真机调试:从"线上没日志"到四层问题定位
前端
likerhood21 小时前
SLF4J: Failed to load class “StaticLoggerBinder“ 解决
java·log4j·maven
Wect21 小时前
LeetCode 97. 交错字符串:动态规划详解
前端·算法·typescript
木斯佳21 小时前
前端八股文面经大全:字节暑期前端一面(2026-04-24)·面经深度解析
前端
凯瑟琳.奥古斯特21 小时前
Redis是什么及核心特性
前端·css·redis·缓存
早日退休!!!1 天前
大模型推理瓶颈七层分析模型
java·服务器·数据库
架构源启1 天前
OpenClaw 只能手动写脚本?我用 Chrome 插件实现了“录制即生成“
前端·人工智能·chrome·自动化
DFT计算杂谈1 天前
VASP官方教程 TRIQS DFT+DMFT计算教程
运维·css·自动化·html·css3
yingyima1 天前
正则表达式实战:如何高效清洗脏数据
前端