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

相关推荐
学海无涯书山有路19 小时前
Android ViewBinding 新手详解(Java 版)—— 结合 ViewModel+LiveData 实战
android·java·开发语言
jaysee-sjc19 小时前
【练习十】Java 面向对象实战:智能家居控制系统
java·开发语言·算法·智能家居
哪里不会点哪里.19 小时前
Spring Boot 启动原理深度解析
java·spring boot·后端
零基础的修炼19 小时前
算法---常见位运算总结
java·开发语言·前端
蜂蜜黄油呀土豆19 小时前
Java虚拟机内存模型解析与内存管理问题
java·jvm·内存管理·内存泄漏·内存溢出
wgslucky19 小时前
sm2 js加密,java服务器端解密
java·开发语言·javascript
Hx_Ma1619 小时前
SpringBoot配置文件占位符
java·spring boot·后端
running up that hill19 小时前
日常刷题记录
java·数据结构·算法
独自破碎E19 小时前
【快手手撕】合并区间
android·java
seven97_top19 小时前
CopyOnWriteArrayList:写时复制机制与高效并发访问
java