将多个 LiveData 值映射为一个
Posted
技术标签:
【中文标题】将多个 LiveData 值映射为一个【英文标题】:Map multiple LiveData values into one 【发布时间】:2018-01-24 14:59:51 【问题描述】:因此,在ios
开发中,我使用ReactiveCocoa
,并且通过该框架,我能够观察多个NSObjects
并将它们组合成一个返回a 值的signal
。像这样的:
-(RACSignal *)modelIsValidSignal
return [RACSignal combineLatest:@[RACObserve(self,username), RACObserve(self,password), RACObserve(self, busyLoggingIn)]
reduce:^id(NSString *username, NSString *password, NSNumber *busyLoggingIn)
return @((username.length > 0) && (password.length > 0 && busyLoggingIn.boolValue == NO));
];
因此,这将返回一个 boolean
,它要么为假要么为真。一旦其中一个对象状态发生变化,就会通知该信号,然后subscriber (Observer)
将获得该布尔值的当前值。
如何使用LiveData
做类似的事情?最接近这样做的是MediatorLiveData
,但是我不知道如何同时观察多个LiveData
事件然后减少它,就像上面的例子一样。
【问题讨论】:
LiveData
专为相当简单的场景而设计。如果你需要这样的东西,请使用 RxJava。
我有点担心将 RxJava 加入其中;因为我试图摆脱对第三部分库的依赖。那么,您是说 LiveData 不可能做到这一点?
“我正在尝试减少对第三部分库的依赖”——LiveData
来自一个库。所有的 android 开发都依赖于库。几乎所有现代软件开发都依赖于库。 “所以,你是说 LiveData 不可能做到这一点?” - 没有什么“开箱即用”可以做到这一点。 MediatorLiveData
支持 N 个源,因此您可以进行所需的转换。我的观点是,谷歌已经表明 RxJava 是复杂反应流的答案。
查看***.com/a/57819928/4685284
【参考方案1】:
我已经做了一些这样的验证
var firstName: MutableLiveData<String> = MutableLiveData()
var lastName: MutableLiveData<String> = MutableLiveData()
var personalDetailsValid: MediatorLiveData<Boolean> = MediatorLiveData()
personalDetailsValid.addSource(firstName,
personalDetailsValid.value = lastName.value?.isNotBlank() == true && it?.isNotBlank() ?: false
)
personalDetailsValid.addSource(lastName,
personalDetailsValid.value = firstName.value?.isNotBlank() == true && it?.isNotBlank() ?: false
)
那我就按平常的方式观察
viewModel.personalDetailsValid.observe(this, Observer
if (it == true)
//Do something
)
我不知道这是否是使用 LiveData 和 MediatorLiveData 的推荐方式,但它对我有用。
【讨论】:
这个案例怎么样:假设我们有两个不同的 LiveData 源。 F.e.我们不知道哪个来源将首先发布数据。此外,我们需要每个来源都知道另一个来源发布的价值。我想必须有一些解决方案。 如您所见,我有两个单独的来源,我分别添加它们来处理这种情况。我们添加了 firstName 的来源,因此对这个变量的任何更改都会得到验证,但也会与 lastName 变量一起使用。同样适用于 lastNameVariable。还是我在你的问题中遗漏了一些东西?【参考方案2】:方便又安全的解决方案:
fun <T1, T2, R> combine(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
combineFn: (value1: T1?, value2: T2?) -> R
): LiveData<R> = MediatorLiveData<R>().apply
addSource(liveData1)
value = combineFn(it, liveData2.value)
addSource(liveData2)
value = combineFn(liveData1.value, it)
fun <T1, T2, R> Pair<LiveData<T1>, LiveData<T2>>.map(
combineFn: (value1: T1?, value2: T2?) -> R
) = combine(first, second, combineFn)
使用示例:
val greetLine = combine(nameLiveData, surnameLiveData) name, surname ->
"Hello, $name $surname!"
// or using map extension function for consistency with LiveData api
val greetLine = (nameLiveData to surnameLiveData).map name, surname ->
"Hello, $name $surname!"
【讨论】:
【参考方案3】:我已经为 2 个(以及更多)LiveData 值实现了一些实用方法。以 RX Observable.combineLatest()
方式工作,这意味着如果观察到的 LiveData
之一将发出任何新值,它将触发更新的值。我已将 Function2
添加为“组合器”函数(在我的情况下,我可以轻松使用 RX.BiFunction,但 LiveData
可以发出 null
值)。
组合来自两个LiveData
的值的示例。
object LiveDataUtils
fun <T1, T2, R> combineLatest(source1: LiveData<T1>,
source2: LiveData<T2>,
combiner: Function2<T1, T2, R>): LiveData<R>
val mediator = MediatorLiveData<R>()
val combinerFunction =
val source1Value = source1.value
val source2Value = source2.value
mediator.value = combiner.apply(source1Value, source2Value)
mediator.addSource(source1) combinerFunction()
mediator.addSource(source2) combinerFunction()
return mediator
interface Function2<T1, T2, R>
fun apply(t1: T1?, t2: T2?): R
【讨论】:
以上是关于将多个 LiveData 值映射为一个的主要内容,如果未能解决你的问题,请参考以下文章
Jetpack之LiveData扩展MediatorLiveData