You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
        }
    }
}

修复说明

  1. 用lifecycleScope替代runBlocking启动协程,不会阻塞主线程,同时协程会和Activity生命周期绑定,页面销毁时自动取消协程,避免内存泄漏
  2. 登录成功后才执行跳转逻辑,时序符合预期
  3. 捕获异常后打印错误日志,方便排查问题
  4. 输入框内容提前在主线程读取后传入登录方法,避免子线程访问View的违规操作
  5. 移除多余的FirebaseAuth.getInstance()重复初始化逻辑,复用已经初始化的auth实例即可

内容的提问来源于stack exchange,提问作者xRawAnger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 03:06:04