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

相关推荐
Fate_I_C17 小时前
Kotlin 内部类和嵌套类
java·开发语言·kotlin
M ? A17 小时前
Vue转React最佳工具对比:Vuera、Veaury与VuReact
前端·javascript·vue.js·经验分享·react.js
We་ct17 小时前
JS手撕:函数进阶 & 设计模式解析
开发语言·前端·javascript·设计模式·面试·前端框架
宸津-代码粉碎机17 小时前
Spring Boot 4.0 实战技巧全解析
java·大数据·spring boot·后端·python
Makoto_Kimur17 小时前
Java Scanner 的 ACM 常用输入模板
java·数据结构·算法
0xDevNull18 小时前
Spring 核心教程:@Component vs @Bean 深度解析
java·后端
小碗羊肉18 小时前
【从零开始学Java | 第三十二篇】方法引用(Method Reference)
java·开发语言
悟空瞎说18 小时前
前端老鸟实战:纯 CSS 实现小红书「真・瀑布流」,零 JS、自动错落、生产可用
前端
yuki_uix18 小时前
当 reduce 遇到二维数据:从"聚合直觉"到"复合 Map"的思维跃迁
前端·javascript·面试
我叫黑大帅18 小时前
Vue3中的computed 与 watch 的区别
前端·javascript·面试