在执行下面的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)
}
}
参考: