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

相关推荐
anOnion26 分钟前
构建无障碍组件之Menu Button pattern
前端·html·交互设计
用户479492835691543 分钟前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
zhangxingchao3 小时前
Kotlin常用的Flow 操作符整理
前端
IT_陈寒5 小时前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
nanxun8866 小时前
记一次诡异的 Docker 容器"串包"故障排查
java
Pedantic6 小时前
SwiftUI 手势笔记
前端·后端
橙子家6 小时前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user20585561518137 小时前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州7 小时前
CSS aspect-ratio 属性完全指南
前端