如何在Android ViewModel中结合RxJava正确处理屏幕方向变化?
如何结合RxJava正确处理ViewModel中的屏幕方向变化?
嘿,我来帮你捋清楚这个问题——你当前的代码有个核心问题:把liveData和disposables放在了companion object里,这会让它们变成静态全局变量,完全违背了ViewModel的生命周期设计初衷。静态变量不会随着ViewModel的重建/销毁而变化,不仅容易造成内存泄漏,还会在多个ViewModel实例之间共享数据,产生意想不到的bug。
下面是正确的处理方式,分步骤来:
1. 将LiveData和Disposables改为ViewModel成员变量
ViewModel本身就是为了在屏幕旋转等配置变化时保留数据和任务的,所以这些变量应该作为ViewModel的普通成员,而非静态全局变量:
class DummyViewModel : ViewModel() { // 跟随ViewModel生命周期的成员变量 val liveData: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() fun test() { disposables.add( Observable.interval(1, TimeUnit.SECONDS) .map { it.toString() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { liveData.value = it } ) }
2. 在ViewModel销毁时清理RxJava订阅
当页面真正被销毁(比如用户退出应用),ViewModel会调用onCleared()方法,这时候必须清空所有RxJava订阅,防止内存泄漏:
override fun onCleared() { super.onCleared() disposables.clear() // 取消所有订阅,避免泄漏 } }
3. 在UI层正确观察LiveData
在Activity或Fragment中,要用viewLifecycleOwner来观察LiveData,这样订阅会自动跟随UI组件的生命周期,屏幕旋转后会自动重新绑定,不会丢失数据:
class MyFragment : Fragment() { private lateinit var viewModel: DummyViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { // 获取ViewModel实例,配置变化时会复用同一个实例 viewModel = ViewModelProvider(this).get(DummyViewModel::class.java) // 观察LiveData,自动感知UI生命周期 viewModel.liveData.observe(viewLifecycleOwner) { countText -> // 这里更新你的UI,比如设置TextView文本 // textView.text = countText } // 启动定时任务 viewModel.test() return inflater.inflate(R.layout.fragment_my, container, false) } }
为什么这样处理有效?
- 屏幕旋转时,ViewModel不会被销毁,RxJava的定时任务会继续运行,LiveData也会保留之前的计数。新的UI实例会自动观察到LiveData的最新值,实现无缝的UI衔接。
- 当页面彻底销毁时,
onCleared()会触发,disposables.clear()会取消所有RxJava订阅,避免订阅持有UI组件的引用导致内存泄漏。 - 去掉静态变量后,每个ViewModel实例都有独立的LiveData和订阅池,不会和其他页面的实例产生数据干扰。
内容的提问来源于stack exchange,提问作者Rgfvfk Iff
相关产品推荐
相关产品推荐

