使用Firebase与Compose导航新页面时应用冻结求助
问题分析
应用冻结和Davey警告的核心原因是页面导航逻辑被重复触发,导致主线程被多次导航操作阻塞:
- 当
createUserResponse变为Success(true)后,Composable每次重组都会执行onNavigateToNewCondoScreen(),引发重复导航 - 重复的导航操作会持续占用主线程资源,导致UI冻结并触发主线程超时的Davey警告
解决方案
1. 用LaunchedEffect控制导航仅执行一次
修改HandleCreateUserResponse,通过LaunchedEffect监听状态变化,确保导航只在Success(true)首次出现时执行:
@Composable fun HandleCreateUserResponse(createUserResponse: Response<Boolean>, onNavigateToNewCondoScreen: () -> Unit) { when(createUserResponse) { is Loading -> ProgressBarVesta() is Success -> { LaunchedEffect(createUserResponse) { if (createUserResponse.data) { onNavigateToNewCondoScreen() } } } is Failure -> Utils.print(createUserResponse.e) } }
2. 重置ViewModel状态避免重复触发
在ViewModel中添加状态重置方法,导航完成后将createUserResponse恢复初始状态,防止后续重组再次触发导航:
@HiltViewModel class SignupViewModel @Inject constructor( private val useCases: UserUseCases ) : ViewModel() { // 原有状态定义... fun resetCreateUserResponse() { createUserResponse = Success(false) } // 原有createUser方法... }
在导航回调中调用该方法(示例基于NavController导航):
fun onNavigateToNewCondoScreen(navController: NavController, viewModel: SignupViewModel) { navController.navigate(Screen.NewCondo.route) viewModel.resetCreateUserResponse() }
3. 可选:改用一次性事件处理导航
如果状态重置的方式不够优雅,可以用MutableSharedFlow实现一次性事件模式,彻底避免状态重复触发:
@HiltViewModel class SignupViewModel @Inject constructor( private val useCases: UserUseCases ) : ViewModel() { // 原有状态定义... private val _navigateToNewCondo = MutableSharedFlow<Unit>(replay = 0) val navigateToNewCondo = _navigateToNewCondo.asSharedFlow() fun createUser(name: String, email: String, password: String) = viewModelScope.launch { // 原有验证逻辑... createUserResponse = Loading val result = useCases.createUser(trimmedName, trimmedEmail, password) createUserResponse = result if (result is Success && result.data) { _navigateToNewCondo.emit(Unit) } } }
在Composable中收集事件执行导航:
@Composable fun SignupScreen(viewModel: SignupViewModel = hiltViewModel(), navController: NavController) { // 原有UI逻辑... LaunchedEffect(Unit) { viewModel.navigateToNewCondo.collect { navController.navigate(Screen.NewCondo.route) } } HandleCreateUserResponse(viewModel.createUserResponse) {} }
额外检查
- Firebase操作部分的
await()是挂起函数,不会阻塞主线程,现有代码逻辑无问题 - 确认
ProgressBarVesta()是轻量实现,未占用过多主线程资源
内容的提问来源于stack exchange,提问作者Luccamvmrey
相关产品推荐
相关产品推荐

