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

Android Kotlin中Firebase.auth.signOut()无法完全登出问题排查

问题背景

在Android Studio中使用Kotlin开发聊天应用,采用Firebase Authentication(手机号验证)实现用户注册功能,需要实现两个需求:

  • 点击MainActivity选项菜单中的登出选项时,应用跳转至PhoneAuthActivity
  • 在SplashActivity中校验用户登录状态,若用户已登录则跳转至MainActivity,未登录则跳转至PhoneAuthActivity

现有实现代码

MainActivity 登出逻辑

override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.search -> {
                Toast.makeText(this, "Search", Toast.LENGTH_SHORT).show()
            }
            R.id.settings -> {
                Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show()
            }
            R.id.update -> {
                Toast.makeText(this, "Update Profile", Toast.LENGTH_SHORT).show()
            }
            R.id.logout -> {
                val alert: AlertDialog = AlertDialog.Builder(this).create()
                alert.setTitle("Logout")
                alert.setMessage("Are you sure you want to logout?")
                alert.setButton(AlertDialog.BUTTON_POSITIVE, "Yes") { dialog, which ->
                    CoroutineScope(Dispatchers.IO).launch{
                        withContext(Dispatchers.Main) {
                            Firebase.auth.signOut()
                        }
                    }
                    val intent = Intent(this@MainActivity,PhoneAuthActivity::class.java)
                    dialog.dismiss()
                    startActivity(intent)
                }
                alert.setButton(AlertDialog.BUTTON_NEGATIVE, "No") { dialog, which ->
                dialog.dismiss()
                }
                alert.show()
            }
        }
        return super.onOptionsItemSelected(item)
    }

SplashActivity 登录状态校验逻辑

import android.animation.Animator
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.anubhav.chatapp.databinding.ActivitySplashBinding
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase

class SplashActivity : AppCompatActivity() {
    private lateinit var binding: ActivitySplashBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivitySplashBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.animationView.addAnimatorListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(p0: Animator?) {
            }
            override fun onAnimationEnd(p0: Animator?) {
                if (Firebase.auth.currentUser != null) {
                    val intent = Intent(applicationContext, MainActivity::class.java)
                    startActivity(intent)
                    finish()
                } else if (Firebase.auth.currentUser == null) {
                    val intent = Intent(applicationContext, PhoneAuthActivity::class.java)
                    startActivity(intent)
                    finish()
                }
            }
            override fun onAnimationCancel(p0: Animator?) {
            }
            override fun onAnimationRepeat(p0: Animator?) {
            }

        })
    }
}

异常表现

登出的表面跳转逻辑看似正常,但存在两个明显问题:

  1. 第一次点击登出后虽然跳转到PhoneAuthActivity,但重启应用时仍会被重定向到MainActivity,不符合已登出应跳转PhoneAuthActivity的预期
  2. 进入错误跳转的MainActivity后再次点击登出会触发应用崩溃,崩溃后用户才会真正完成登出,相当于需要两次点击才能走完完整登出流程
根因分析

问题核心是登出逻辑的协程使用完全错误:

  1. Firebase.auth.signOut()本身是轻量同步方法,不需要切换IO线程执行。CoroutineScope(Dispatchers.IO).launch是非阻塞启动协程,协程启动后代码会立刻向下执行跳转逻辑,而协程内部需要通过withContext(Dispatchers.Main)等待主线程调度才能执行signOut(),此时页面已经跳转到PhoneAuthActivity,登出操作根本没有执行,本地缓存的用户登录态完全没有被清除。
  2. 重启应用时SplashActivity检测到currentUser不为空,自然会跳转到MainActivity。第一次点击登出时启动的协程一直持有旧MainActivity实例的引用,等第二次进入MainActivity再次点击登出时,旧协程执行时引用的Dialog、View等组件已经销毁,直接触发空指针崩溃。崩溃后应用进程被系统回收,内存中缓存的登录态被清空,下次启动才会走到未登录分支。
修复方案
  1. 删掉多余的协程逻辑,signOut()直接在主线程同步调用,等登出完成后再执行跳转,同时清空Activity返回栈,避免按返回键回到已登录状态的MainActivity:
R.id.logout -> {
    val alert: AlertDialog = AlertDialog.Builder(this).create()
    alert.setTitle("Logout")
    alert.setMessage("Are you sure you want to logout?")
    alert.setButton(AlertDialog.BUTTON_POSITIVE, "Yes") { dialog, which ->
        // 同步执行登出,无需协程
        Firebase.auth.signOut()
        dialog.dismiss()
        val intent = Intent(this@MainActivity,PhoneAuthActivity::class.java)
        // 清空返回栈
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        startActivity(intent)
        finish()
    }
    alert.setButton(AlertDialog.BUTTON_NEGATIVE, "No") { dialog, which ->
        dialog.dismiss()
    }
    alert.show()
}
  1. 简化SplashActivity的冗余判断逻辑,跳转时同样清空返回栈:
override fun onAnimationEnd(p0: Animator?) {
    val target = if (Firebase.auth.currentUser != null) MainActivity::class.java else PhoneAuthActivity::class.java
    startActivity(Intent(applicationContext, target).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    })
    finish()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:12:23