Android Studio 5中RxJava2的PublishSubject未触发subscribe回调问题
问题原因分析
你遇到的问题核心在于PublishSubject的特性:它是一个"热"的Observable,但只会将订阅之后发送的事件推送给观察者,不会保存订阅之前发送的任何事件。
你的代码执行顺序是这样的:
- Activity调用
viewModelProvider.get(FilmsRxJavaViewModel::class.java),ViewModel被初始化,触发init块:- 发送
isShowProgress.onNext(true) - 调用
loadData()发送isShowProgress.onNext(false)
- 发送
- 之后Activity才对
isShowProgress进行订阅
这两步的顺序导致ViewModel发送的true和false事件都发生在订阅之前,自然不会被Activity的观察者接收到,所以Logcat里看不到订阅回调的日志。
解决方案
针对这个场景,有两种常见的解决思路:
1. 改用BehaviorSubject替代PublishSubject
BehaviorSubject会保存最后一次发送的事件,当新的观察者订阅时,会立即收到这个最后事件,后续的事件也能正常接收。非常适合这种需要让订阅者获取最新状态的场景。
修改ViewModel的代码:
import io.reactivex.subjects.BehaviorSubject class FilmsRxJavaViewModel(application: Application) : AndroidViewModel(application) { // 可以设置默认值,或者用BehaviorSubject.create(),根据需求选择 var isShowProgress: BehaviorSubject<Boolean> = BehaviorSubject.createDefault(false) init { Debug.d(TAG, "init:") isShowProgress.onNext(true) loadData() } fun loadData() { Debug.d(TAG, "loadData") isShowProgress.onNext(false) } }
这样当Activity订阅时,会立即收到ViewModel最后发送的false事件,Logcat就能打印出对应的回调日志了。
2. 调整事件发送的时机
如果不想更换Subject类型,可以调整逻辑,让ViewModel在订阅完成后再发送事件。比如:
- 修改ViewModel,不在
init块里自动调用loadData():
init { Debug.d(TAG, "init:") // 移除自动调用loadData()的逻辑 }
- 在Activity订阅完成后,手动触发ViewModel的数据加载:
private fun initLogic() { val viewModelProvider = ViewModelProviders.of(this) filmsRxJavaViewModel = viewModelProvider.get(FilmsRxJavaViewModel::class.java) val dispose = filmsRxJavaViewModel.isShowProgress .subscribe { it -> Debug.d(TAG, "initLogic: isShowProgress = $it") } // 订阅完成后再触发事件发送 filmsRxJavaViewModel.isShowProgress.onNext(true) filmsRxJavaViewModel.loadData() }
这样事件都是在订阅之后发送的,观察者就能正常接收到了。
额外注意事项
记得在Activity的onDestroy()方法中取消订阅,避免内存泄漏:
private var dispose: Disposable? = null private fun initLogic() { // ... 其他代码 dispose = filmsRxJavaViewModel.isShowProgress.subscribe(...) } override fun onDestroy() { super.onDestroy() dispose?.dispose() }
内容的提问来源于stack exchange,提问作者Alexei
相关产品推荐
相关产品推荐

