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() } } } }
问题原因
- LiveData订阅自动失效:当
activityCounter == 0时,当前Activity已进入onStop阶段,LiveData的observe方法会自动在Activity进入STOPPED状态时取消订阅,导致authListener.postValue发出的事件无法被接收,processResponse自然不会触发。 - 自定义ViewModel单例问题:手动实现的
ViewModelBase单例脱离了官方ViewModelProvider的生命周期管理,进一步加剧了订阅失效和生命周期不匹配的问题。 - 应用关闭时资源回收:应用关闭时系统会快速回收资源,异步登出操作完成后,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绑定,同时调整应用生命周期监听逻辑:
- 修改
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() { } }) } }
- 修改
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() } } } }
- 改用
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
相关产品推荐
相关产品推荐

