Firebase Authentication导致界面卡顿问题排查与优化需求
遇到Firebase Authentication导致界面卡顿的问题,不清楚原因。以下是MainActivity.kt、AuthState可组合项、AuthViewModel及EmailAuthRepositoryImpl的代码,希望解决卡顿问题,让登录界面更流畅,同时排查其他潜在问题并获取优化建议。
MainActivity.kt
class MainActivity : ComponentActivity() { private lateinit var navHostController: NavHostController private val deepLinkViewModel: DeepLinkViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { KoinContext { CountryAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { navHostController = rememberNavController() deepLinkViewModel.handleDeepLink(intent, navHostController) NavGraph( navHostController = navHostController, ) AuthState(navHostController = navHostController) } } } } } }
AuthState 可组合项
@Composable fun AuthState( navHostController: NavHostController, viewModel: AuthViewModel = koinViewModel(), ) { val isUserSignedOut = viewModel.getAuthState().collectAsState().value if (isUserSignedOut) { Log.i(TAG, "viewModel.isUserSignedOut: $isUserSignedOut ") NavigateToSignInScreen(navController = navHostController) } else { if (viewModel.isEmailVerified) { Log.i(TAG, "viewModel.isEmailVerified: ${viewModel.isEmailVerified}") NavigateToMapsScreen(navController = navHostController) } else { Log.i(TAG, "viewModel.isEmailNotVerified: ${viewModel.isEmailVerified}") NavigateToVerifyEmailScreen(navController = navHostController) } } } @Composable private fun NavigateToSignInScreen(navController: NavController) = navController.navigate(Screen.SignInScreen.route) { popUpTo(navController.graph.id) { inclusive = true } } @Composable private fun NavigateToMapsScreen(navController: NavController) = navController.navigate(Screen.TabLayoutScreen(args = "/${Locale.getDefault().displayCountry}").route) { popUpTo(navController.graph.id) { inclusive = true } } @Composable private fun NavigateToVerifyEmailScreen(navController: NavController) = navController.navigate( Screen.VerifyEmailScreen.route ) { popUpTo(navController.graph.id) { inclusive = true } }
AuthViewModel
class AuthViewModel( private val repo: EmailAuthRepository ): ViewModel() { init { getAuthState() } fun getAuthState() = repo.getAuthState(viewModelScope = viewModelScope) val isEmailVerified get() = repo.currentUser?.isEmailVerified ?: false }
EmailAuthRepositoryImpl
class EmailAuthRepositoryImpl( private val auth: FirebaseAuth, private val db: FirebaseFirestore, ) : EmailAuthRepository { override val currentUser: FirebaseUser? get() = auth.currentUser override fun getAuthState(viewModelScope: CoroutineScope) = callbackFlow { val authStateListener = AuthStateListener { auth - trySend(auth.currentUser == null) } auth.addAuthStateListener(authStateListener) awaitClose { auth.removeAuthStateListener(authStateListener) } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), auth.currentUser == null) }
修复导航重复触发导致的卡顿:当前AuthState直接在Composable代码块中调用导航方法,每次重组都会执行导航逻辑,引发频繁跳转和界面卡顿。需用
LaunchedEffect包裹导航逻辑,仅在状态变化时触发一次:@Composable fun AuthState( navHostController: NavHostController, viewModel: AuthViewModel = koinViewModel(), ) { val isUserSignedOut = viewModel.getAuthState().collectAsState().value val isEmailVerified = viewModel.getEmailVerifiedState().collectAsState().value LaunchedEffect(isUserSignedOut, isEmailVerified) { when { isUserSignedOut -> { Log.i(TAG, "viewModel.isUserSignedOut: $isUserSignedOut ") navHostController.navigate(Screen.SignInScreen.route) { popUpTo(navHostController.graph.startDestinationRoute!!) { inclusive = true } } } isEmailVerified -> { Log.i(TAG, "viewModel.isEmailVerified: $isEmailVerified") navHostController.navigate(Screen.TabLayoutScreen(args = "/${Locale.getDefault().displayCountry}").route) { popUpTo(navHostController.graph.startDestinationRoute!!) { inclusive = true } } } else -> { Log.i(TAG, "viewModel.isEmailNotVerified: $isEmailVerified") navHostController.navigate(Screen.VerifyEmailScreen.route) { popUpTo(navHostController.graph.startDestinationRoute!!) { inclusive = true } } } } } }修复Repository语法错误:
AuthStateListener的lambda表达式写法有误,auth -应改为auth ->,否则会引发编译错误或逻辑异常:val authStateListener = AuthStateListener { auth -> trySend(auth.currentUser == null) }实现验证状态的响应式更新:当前
isEmailVerified直接读取currentUser属性,无法在用户完成邮箱验证后自动更新UI。需将其改为Flow,同步AuthState变化:// EmailAuthRepository中新增方法 override fun getEmailVerifiedState(viewModelScope: CoroutineScope): Flow<Boolean> = callbackFlow { val authStateListener = AuthStateListener { auth -> trySend(auth.currentUser?.isEmailVerified ?: false) } auth.addAuthStateListener(authStateListener) awaitClose { auth.removeAuthStateListener(authStateListener) } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), auth.currentUser?.isEmailVerified ?: false) // AuthViewModel中暴露Flow fun getEmailVerifiedState() = repo.getEmailVerifiedState(viewModelScope)移除ViewModel无用初始化代码:
init { getAuthState() }无实际作用,因为getAuthState()返回的Flow需要UI层collect才会生效,直接调用不会触发逻辑,可删除。修复DeepLink重复处理问题:MainActivity中直接在Composable里调用
deepLinkViewModel.handleDeepLink,每次重组都会执行,可能导致重复处理DeepLink。用LaunchedEffect包裹,仅初始化时执行一次:navHostController = rememberNavController() LaunchedEffect(Unit) { deepLinkViewModel.handleDeepLink(intent, navHostController) }优化Flow订阅策略:将Repository中
stateIn的SharingStarted.WhileSubscribed()调整为SharingStarted.WhileSubscribed(5000L),用户离开界面后延迟5秒取消订阅,减少不必要的资源占用。
内容的提问来源于stack exchange,提问作者wenubey

