如何解决Kotlin Compose认证时CircularProgressIndicator卡顿问题
解决CircularProgressIndicator冻结问题的方案
核心问题分析
你的卡顿问题完全是主线程被阻塞导致的:
- Google登录回调中使用
runBlocking,会直接挂起主线程直到所有内部操作完成,期间UI无法刷新,ProgressIndicator自然冻结。 - Facebook登录的协程默认在主线程执行,如果
signInWithFacebookLogin包含网络/数据库等耗时操作,同样会占用主线程导致UI卡顿。
CircularProgressIndicator不需要独立线程,Compose的UI渲染本身就在主线程,只要主线程不被阻塞,它就能流畅运行。
具体代码修改
1. 修复Google登录的主线程阻塞问题
把runBlocking替换为viewModelScope.launch,避免阻塞主线程:
@Composable fun activityGoogleResultLauncher(): ActivityResultLauncher<Intent> { val loginViewModel = LocalUserState.current val startAutocomplete: ActivityResultLauncher<Intent> = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> val completedTask: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(result.data) try { if (completedTask.isSuccessful) { val account: GoogleSignInAccount? = completedTask.getResult(ApiException::class.java) val email = account?.email val token: String = account?.idToken!! // 替换runBlocking为viewModelScope.launch,避免阻塞主线程 loginViewModel.viewModelScope.launch { loginViewModel.singInWithGoogle(token) loginViewModel.getUserType = true loginViewModel.loginStatus = "in" FirebaseMessaging.getInstance().token.addOnCompleteListener { loginViewModel.insertFCMWhenLogin(it.result) } email?.let { loginViewModel.updateEmail(it) } } } else { println("AUTH Google Auth failed: ${completedTask.exception}") } } catch (e: ApiException) { println("AUTH Failed to authenticate using Google OAuth: " + e.message) } } return startAutocomplete }
2. 确保ViewModel中的登录方法在后台线程执行
修改singInWithGoogle和signInWithFacebookLogin方法,用withContext(Dispatchers.IO)包裹耗时操作:
// 在LoginViewModel中 suspend fun singInWithGoogle(token: String) { withContext(Dispatchers.IO) { // 这里执行Firebase Google认证等耗时操作 // 示例:firebaseAuth.signInWithCredential(GoogleAuthProvider.getCredential(token, null)) } } suspend fun signInWithFacebookLogin(token: String) { withContext(Dispatchers.IO) { // 这里执行Facebook认证等耗时操作 // 示例:firebaseAuth.signInWithCredential(FacebookAuthProvider.getCredential(token)) } }
3. 优化Facebook登录的协程执行
直接使用viewModelScope管理协程,避免主线程阻塞:
LoginManager.getInstance().registerCallback( callbackManager, object : FacebookCallback<LoginResult> { override fun onSuccess(result: LoginResult) { val accessToken = result.accessToken // 使用viewModelScope确保协程生命周期与ViewModel绑定,且不阻塞主线程 loginViewModel.viewModelScope.launch { loginViewModel.signInWithFacebookLogin(accessToken.token) loginViewModel.getUserType = true loginViewModel.loginStatus = "in" FirebaseMessaging.getInstance().token.addOnCompleteListener { loginViewModel.insertFCMWhenLogin(it.result) } } } override fun onCancel() { println("AUTH Cancelled Facebook login") loginViewModel.loadingLogin = false } override fun onError(error: FacebookException) { println("AUTH Failed to authenticate with Facebook: ${error.message}") loginViewModel.loadingLogin = false } }, )
关键注意事项
- 永远不要在主线程执行耗时操作:网络请求、数据库操作、第三方SDK的同步认证逻辑都要放到
Dispatchers.IO线程。 - 避免使用runBlocking:它会阻塞调用线程,在UI相关代码中绝对禁止使用,改用
viewModelScope.launch或lifecycleScope.launch。 - ViewModel的suspend函数要处理线程:ViewModel中的suspend函数应该内部用
withContext切换到合适的线程,对外暴露的suspend函数让调用方无需关心线程细节。
内容的提问来源于stack exchange,提问作者Cipri
相关产品推荐
相关产品推荐

