You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose调用hide()时底部弹窗卡顿及JobCancellationException问题求助

问题:Compose ModalBottomSheetLayout调用hide()周期性触发取消异常并导致Overlay挂起

在使用Android Compose的ModalBottomSheetLayout时,调用rememberModalBottomSheetState的hide()方法会周期性导致界面Overlay挂起,抛出异常:

CancellationException nested kotlinx.coroutines.JobCancellationException: ScopeCoroutine was cancelled; job=ScopeCoroutine{Cancelled}@5ef8661

求问:异常原因是什么?如何高效重试隐藏弹窗?

附代码示例:

@Composable
fun ExampleUi(
    exampleBotttomSheetState: ExampleBottomSheetState,
    coroutineScope: CoroutineScope
) {
    
    val bottomSheetScaffoldState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden
    )
    
    when (exampleBotttomSheetState.value) {
        is ExampleBottomSheetState.Hide -> {
            coroutineScope.launch {
                bottomSheetScaffoldState.hide()
            }
        }
        is ExampleBottomSheetState.Show -> {
            coroutineScope.launch {
                bottomSheetScaffoldState.show()
            }
        }
    }


    ModalBottomSheetLayout(
        sheetState = bottomSheetScaffoldState,
        sheetContent = {
            // Bottom Sheet Content UI
        }
    ) {
        // UI content
    }
}

异常原因

  • 协程作用域提前取消:传入的coroutineScope可能在hide()执行期间被取消(比如组件重组、页面销毁或作用域生命周期结束),导致挂起的动画协程中断,抛出取消异常。
  • 重复操作触发冲突:如果exampleBotttomSheetState频繁切换Hide/Show状态,多次调用hide()/show()会引发协程竞争,后启动的操作可能取消前一个未完成的动画协程。
  • 状态不一致:当弹窗已经处于Hidden状态时仍调用hide(),底层协程因无实际操作可执行会被取消,触发异常。

高效修复与重试方案

1. 先校验状态再执行操作

调用hide()/show()前先判断当前弹窗状态,避免重复操作:

is ExampleBottomSheetState.Hide -> {
    coroutineScope.launch {
        if (bottomSheetScaffoldState.currentValue != ModalBottomSheetValue.Hidden) {
            bottomSheetScaffoldState.hide()
        }
    }
}
is ExampleBottomSheetState.Show -> {
    coroutineScope.launch {
        if (bottomSheetScaffoldState.currentValue != ModalBottomSheetValue.Expanded) {
            bottomSheetScaffoldState.show()
        }
    }
}

2. 捕获异常并按需重试

在协程中捕获取消异常,仅当弹窗仍处于显示状态时重试:

is ExampleBottomSheetState.Hide -> {
    coroutineScope.launch {
        try {
            bottomSheetScaffoldState.hide()
        } catch (e: JobCancellationException) {
            if (bottomSheetScaffoldState.currentValue != ModalBottomSheetValue.Hidden) {
                delay(100) // 加短暂延迟避免频繁重试
                bottomSheetScaffoldState.hide()
            }
        }
    }
}

3. 改用LaunchedEffect管理协程

放弃外部coroutineScope,使用Compose内置的LaunchedEffect绑定状态变化,它会自动处理协程的生命周期,避免作用域取消问题:

val bottomSheetScaffoldState = rememberModalBottomSheetState(
    initialValue = ModalBottomSheetValue.Hidden
)

LaunchedEffect(exampleBotttomSheetState) {
    when (exampleBotttomSheetState.value) {
        is ExampleBottomSheetState.Hide -> {
            if (bottomSheetScaffoldState.currentValue != ModalBottomSheetValue.Hidden) {
                bottomSheetScaffoldState.hide()
            }
        }
        is ExampleBottomSheetState.Show -> {
            if (bottomSheetScaffoldState.currentValue != ModalBottomSheetValue.Expanded) {
                bottomSheetScaffoldState.show()
            }
        }
    }
}

4. 确保协程作用域生命周期匹配

如果必须使用外部coroutineScope,选择和弹窗所在组件生命周期一致的作用域(比如viewModelScope),避免作用域提前被取消。

内容的提问来源于stack exchange,提问作者CJMobileApps

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 11:10:50