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
相关产品推荐
相关产品推荐

