打开DialogFragment触发屏幕旋转时Observer多次触发的问题如何解决?
问题根因
- 横竖屏切换触发了Activity销毁重建,原生
MutableLiveData本身是粘性事件,会向新注册的Observer推送缓存的历史值,导致重建后再次触发旧数据的回调 - DialogFragment关闭时切回横屏触发Activity重建,此时你在
onCreate中每次都会重新注册Observer,缓存的inputBarcode值会再次被回调,就出现了多次触发postInput的问题
解决方案
方案1:使用SingleLiveEvent处理一次性事件
SingleLiveEvent是专门用于处理单次回调场景的LiveData实现,只会通知一次新的观察者,不会重复推送历史缓存值。
首先实现SingleLiveEvent类:
class SingleLiveEvent<T> : MutableLiveData<T>() { private val pending = AtomicBoolean(false) override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { super.observe(owner) { t -> if (pending.compareAndSet(true, false)) { observer.onChanged(t) } } } override fun setValue(t: T?) { pending.set(true) super.setValue(t) } override fun postValue(t: T?) { pending.set(true) super.postValue(t) } }
然后把ViewModel里的inputBarcode类型从MutableLiveData改为SingleLiveEvent即可,其他现有调用逻辑不用修改。
方案2:使用事件包装类手动标记消费状态
自定义事件包装类,通过标记位控制事件仅被消费一次:
open class Event<out T>(private val content: T) { var hasBeenHandled = false private set // 仅返回未消费的内容 fun getContentIfNotHandled(): T? { return if (hasBeenHandled) { null } else { hasBeenHandled = true content } } }
修改ViewModel中的inputBarcode定义:
val inputBarcode = MutableLiveData<Event<String>>()
发送事件时改为包装事件:
// sendInputInvoice方法内部修改为 inputBarcode.value = Event(invoiceInput)
Observer接收时判断是否已消费:
sharedViewModel.inputBarcode.observe(this) { event -> event.getContentIfNotHandled()?.let { inputValue -> postInput(inputValue) } }
方案3:禁止Activity配置变更重建(可选,不优先推荐)
如果你的Activity在横竖屏切换时不需要重新加载布局,可在Manifest对应的Activity节点添加配置,避免切屏时销毁重建:
<activity android:name=".YourActivity" android:configChanges="orientation|screenSize|screenLayout" android:screenOrientation="landscape"/>
该方案改造成本最低,但仅适用于横竖屏布局完全一致的场景,可能会隐藏其他配置变更带来的问题,不推荐通用场景使用。
内容的提问来源于stack exchange,提问作者Luis Botero
相关产品推荐
相关产品推荐

