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

Android中postValue()未触发LiveData observe的问题排查

问题描述

我希望在应用关闭时,完成Firebase Auth登出操作后,执行ActivityBase中的processResponse(...)方法做后续处理。目前登出操作已经完成,但应用关闭后processResponse方法并未被调用。我的应用采用MVVM架构(我刚接触这个架构)。

核心代码

UserServiceSignOutImpl 实现类

class UserServiceSignOutImpl : UserServiceSignOut, AuthStateChangeProcessing {
    private val auth = FirebaseAuth.getInstance()

    override fun signOut(): Observable<AuthListener> {
        return Observable.create { emitter: ObservableEmitter<AuthListener> ->
            MyAuthStateListener(auth).getAuthStateListener(emitter, this)
        }
    }

    override fun onProcessing(emitter: ObservableEmitter<AuthListener>) {
        emitter.onNext(AuthListener.success("Logged out successfully."))
    }
}

ViewModelBase 视图模型

class ViewModelBase : ViewModel() {
    val authListener: MutableLiveData<AuthListener> = MutableLiveData()
    private val userServiceSignOut: UserServiceSignOut = UserServiceSignOutImpl()

    companion object {

        private lateinit var instance: ViewModelBase

        @MainThread
        fun getInstance(): ViewModelBase {
            instance = if (::instance.isInitialized) instance else ViewModelBase()
            return instance
        }

    }

    fun signOut() {
        userServiceSignOut.signOut().subscribe(object : Observer<AuthListener> {
            override fun onSubscribe(d: Disposable) {
            }

            override fun onNext(_authListener: AuthListener) {
                authListener.postValue(_authListener)
            }

            override fun onError(e: Throwable) {
                authListener.postValue(error(e.message!!))
            }

            override fun onComplete() {
            }
        })
    }
}

MyApp 应用生命周期监听

class MyApp : Application(), ActivityLifecycleCallbacks {

    private var activityCounter = 0

    override fun onCreate() {
        super.onCreate()

        registerActivityLifecycleCallbacks(this)
    }

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

    override fun onActivityStarted(activity: Activity) {
        activityCounter++
    }

    override fun onActivityResumed(activity: Activity) {}

    override fun onActivityPaused(activity: Activity) {}

    override fun onActivityStopped(activity: Activity) {
        activityCounter--
        if (activityCounter == 0) {
            (activity as ActivityBase).viewModel.signOut()
        }
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

    override fun onActivityDestroyed(activity: Activity) {}
}

ActivityBase 基类Activity

abstract class ActivityBase : AppCompatActivity() {

    lateinit var viewModel: ViewModelBase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelBase.getInstance()
        viewModel.authListener.observe(this) { authListener: AuthListener -> processResponse(authListener) }
    }
    
    private fun processResponse(authListener: AuthListener) {
        when (authListener.status) {
            AuthListener.Status.SUCCESS -> {
                Toast.makeText(this, "You have been logged out successfully.", Toast.LENGTH_SHORT).show()
            }

            AuthListener.Status.ERROR -> {
                Toast.makeText(this, "There was a problem", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

问题原因

  1. LiveData订阅自动失效:当activityCounter == 0时,当前Activity已进入onStop阶段,LiveData的observe方法会自动在Activity进入STOPPED状态时取消订阅,导致authListener.postValue发出的事件无法被接收,processResponse自然不会触发。
  2. 自定义ViewModel单例问题:手动实现的ViewModelBase单例脱离了官方ViewModelProvider的生命周期管理,进一步加剧了订阅失效和生命周期不匹配的问题。
  3. 应用关闭时资源回收:应用关闭时系统会快速回收资源,异步登出操作完成后,Activity可能已被销毁,无法响应回调。

解决方案

方案1:使用observeForever保持订阅(临时应急)

将ActivityBase中的observe替换为observeForever,确保Activity进入后台后仍能接收事件,同时在销毁时手动取消订阅避免内存泄漏:

abstract class ActivityBase : AppCompatActivity() {

    lateinit var viewModel: ViewModelBase
    private val authObserver = Observer<AuthListener> { authListener -> 
        processResponse(authListener) 
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelBase.getInstance()
        viewModel.authListener.observeForever(authObserver)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        viewModel.authListener.removeObserver(authObserver)
    }
    
    private fun processResponse(authListener: AuthListener) {
        when (authListener.status) {
            AuthListener.Status.SUCCESS -> {
                Toast.makeText(this, "已成功登出", Toast.LENGTH_SHORT).show()
            }

            AuthListener.Status.ERROR -> {
                Toast.makeText(this, "出现问题", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

方案2:遵循MVVM规范,使用官方ViewModelProvider

替换自定义单例为官方ViewModelProvider,确保ViewModel生命周期与Activity绑定,同时调整应用生命周期监听逻辑:

  1. 修改ViewModelBase,移除自定义单例:
class ViewModelBase : ViewModel() {
    val authListener: MutableLiveData<AuthListener> = MutableLiveData()
    private val userServiceSignOut: UserServiceSignOut = UserServiceSignOutImpl()

    fun signOut() {
        userServiceSignOut.signOut().subscribe(object : Observer<AuthListener> {
            override fun onSubscribe(d: Disposable) {
            }

            override fun onNext(_authListener: AuthListener) {
                authListener.postValue(_authListener)
            }

            override fun onError(e: Throwable) {
                authListener.postValue(error(e.message!!))
            }

            override fun onComplete() {
            }
        })
    }
}
  1. 修改ActivityBase,用viewModels()获取ViewModel:
abstract class ActivityBase : AppCompatActivity() {

    val viewModel: ViewModelBase by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel.authListener.observe(this) { authListener -> 
            processResponse(authListener) 
        }
    }
    
    private fun processResponse(authListener: AuthListener) {
        when (authListener.status) {
            AuthListener.Status.SUCCESS -> {
                Toast.makeText(this, "已成功登出", Toast.LENGTH_SHORT).show()
            }

            AuthListener.Status.ERROR -> {
                Toast.makeText(this, "出现问题", Toast.LENGTH_SHORT).show()
            }
        }
    }
}
  1. 改用ProcessLifecycleOwner监听应用全局生命周期:
class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onStop(owner: LifecycleOwner) {
                super.onStop(owner)
                val viewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(this).create(ViewModelBase::class.java)
                viewModel.signOut()
            }
        })
    }
}

方案3:同步登出+即时回调

Firebase Auth的signOut()本身是同步方法,可直接执行后触发回调,无需依赖异步流:
修改UserServiceSignOutImpl:

class UserServiceSignOutImpl : UserServiceSignOut {
    private val auth = FirebaseAuth.getInstance()

    override fun signOut(): AuthListener {
        return try {
            auth.signOut()
            AuthListener.success("Logged out successfully.")
        } catch (e: Exception) {
            AuthListener.error(e.message ?: "登出失败")
        }
    }
}

修改ViewModelBase的signOut方法:

fun signOut() {
    val result = userServiceSignOut.signOut()
    authListener.postValue(result)
}

注意事项

  • 避免在应用关闭时执行依赖UI的操作(如Toast),系统可能已回收UI资源导致无法显示,可改用本地存储标记,下次启动时提示。
  • 严格遵循MVVM规范,使用官方ViewModelProvider管理ViewModel生命周期,避免自定义单例引发的内存泄漏或回调失效问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 01:00:10