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?) { } }) } }
异常表现
登出的表面跳转逻辑看似正常,但存在两个明显问题:
- 第一次点击登出后虽然跳转到PhoneAuthActivity,但重启应用时仍会被重定向到MainActivity,不符合已登出应跳转PhoneAuthActivity的预期
- 进入错误跳转的MainActivity后再次点击登出会触发应用崩溃,崩溃后用户才会真正完成登出,相当于需要两次点击才能走完完整登出流程
根因分析
问题核心是登出逻辑的协程使用完全错误:
Firebase.auth.signOut()本身是轻量同步方法,不需要切换IO线程执行。CoroutineScope(Dispatchers.IO).launch是非阻塞启动协程,协程启动后代码会立刻向下执行跳转逻辑,而协程内部需要通过withContext(Dispatchers.Main)等待主线程调度才能执行signOut(),此时页面已经跳转到PhoneAuthActivity,登出操作根本没有执行,本地缓存的用户登录态完全没有被清除。- 重启应用时SplashActivity检测到
currentUser不为空,自然会跳转到MainActivity。第一次点击登出时启动的协程一直持有旧MainActivity实例的引用,等第二次进入MainActivity再次点击登出时,旧协程执行时引用的Dialog、View等组件已经销毁,直接触发空指针崩溃。崩溃后应用进程被系统回收,内存中缓存的登录态被清空,下次启动才会走到未登录分支。
修复方案
- 删掉多余的协程逻辑,
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() }
- 简化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
相关产品推荐
相关产品推荐

