Kotlin中Firebase Auth配合协程实现登录时应用崩溃如何解决?
问题原因及修复方案
存在的问题
- 核心问题1:错误使用
runBlocking阻塞主线程
点击事件运行在Android主线程,你直接调用runBlocking会阻塞当前线程等待内部协程执行完成,Firebase登录是网络耗时操作,长时间阻塞主线程会导致界面冻结,超过系统阈值就触发ANR(应用无响应)崩溃,你看到的tombstoned日志就是ANR产生的崩溃堆栈记录。 - 核心问题2:登录逻辑时序错误
你调用登录方法后直接弹出成功提示、跳转到主页,完全没有等待登录请求的返回结果,无论登录成功或失败都会执行跳转,逻辑完全不符合预期。 - 核心问题3:异常被静默捕获
登录方法的catch块是空实现,所有登录过程产生的异常(密码错误、网络异常、账号不存在等)都被直接吞掉,出现问题无法从日志排查错误原因。 - 隐患问题:子线程访问View
你在Dispatchers.IO的子线程上下文里直接读取binding的输入框内容,Android视图组件仅允许在主线程访问,虽然当前场景未直接崩溃,但属于违规操作,随时可能引发闪退。
修复方案
首先需要在模块级build.gradle中引入生命周期协程扩展依赖(已引入可忽略):
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
修复后完整代码如下:
class LoginActivity : AppCompatActivity() { private lateinit var auth: FirebaseAuth private lateinit var binding: ActivityLoginBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLoginBinding.inflate(layoutInflater) setContentView(binding.root) auth = Firebase.auth init() } private fun init() { binding.loginButton.setOnClickListener { loginValidator() } binding.loginNotRegisteredText.setOnClickListener { val intent = Intent(this, RegisterActivity::class.java) startActivity(intent) } } private fun loginValidator(){ val email = binding.loginEmailEditText.text.toString() val pass = binding.loginPasswordEditText.text.toString() if (email.isEmpty() || pass.isEmpty()) { Toast.makeText(this, "请填写所有表单项", Toast.LENGTH_SHORT).show() return } // 使用lifecycleScope启动协程,和Activity生命周期绑定,不会造成内存泄漏 lifecycleScope.launch { val loginSuccess = loginWithEmail(auth, email, pass) if (loginSuccess) { Log.d("登录", "登录成功") Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show() startActivity(Intent(this@LoginActivity, MainActivity::class.java)) finish() } else { Toast.makeText(this@LoginActivity, "登录失败,请检查账号密码或网络", Toast.LENGTH_SHORT).show() } } } // 账号密码提前在主线程读取后作为参数传入,避免子线程访问View private suspend fun loginWithEmail( firebaseAuth: FirebaseAuth, email: String, pass: String ): Boolean { return try { // Firebase提供的await是挂起函数,内部已处理线程调度,无需手动切Dispatchers.IO firebaseAuth.signInWithEmailAndPassword(email, pass).await() true } catch (e: Exception) { Log.e("登录错误", "登录失败", e) false } } }
修复说明
- 用
lifecycleScope替代runBlocking启动协程,不会阻塞主线程,同时协程会和Activity生命周期绑定,页面销毁时自动取消协程,避免内存泄漏 - 登录成功后才执行跳转逻辑,时序符合预期
- 捕获异常后打印错误日志,方便排查问题
- 输入框内容提前在主线程读取后传入登录方法,避免子线程访问View的违规操作
- 移除多余的
FirebaseAuth.getInstance()重复初始化逻辑,复用已经初始化的auth实例即可
内容的提问来源于stack exchange,提问作者xRawAnger
相关产品推荐
相关产品推荐

