我使用以下类来转换许多不同类型的实时数据。
class MultiMapLiveData<T>(
private val liveDataSources: Array<LiveData<*>>,
private val waitFirstValues: Boolean = true,
private val transform: (signalledLiveData: LiveData<*>) -> T
): LiveData<T>() {
private val mObservers = ArrayList<Observer<Any>>()
private var mInitializedSources = mutableSetOf<LiveData<*>>()
override fun onActive() {
super.onActive()
if (mObservers.isNotEmpty()) throw InternalError(REACTIVATION_ERROR_MESSAGE)
if (mInitializedSources.isNotEmpty()) throw InternalError(REACTIVATION_ERROR_MESSAGE)
for (t in liveDataSources.indices) {
val liveDataSource = liveDataSources[t]
val observer = Observer<Any> {
if (waitFirstValues) {
if (mInitializedSources.size < liveDataSources.size) {
mInitializedSources.add(liveDataSource)
}
if (mInitializedSources.size == liveDataSources.size) {
value = transform(liveDataSource)
}
} else {
value = transform(liveDataSource)
}
}
liveDataSource.observeForever(observer)
mObservers.add(observer)
}
}
override fun onInactive() {
super.onInactive()
for (t in liveDataSources.indices) {
val liveDataSource = liveDataSources[t]
val observer = mObservers[t]
liveDataSource.removeObserver(observer)
}
mObservers.clear()
mInitializedSources.clear()
}
companion object {
private const val REACTIVATION_ERROR_MESSAGE = "Reactivation of active LiveData"
}
}
class MyTransformations {
companion object {
fun <T> multiMap(
liveDataSources: Array<LiveData<*>>,
waitFirstValues: Boolean = true,
transform: (signalledLiveData: LiveData<*>) -> T
): LiveData<T> {
return MultiMapLiveData(liveDataSources, waitFirstValues, transform)
}
fun <T> multiSwitch(
liveDataSources: Array<LiveData<*>>,
waitFirstValues: Boolean = true,
transform: (signalledLiveData: LiveData<*>) -> LiveData<T>
): LiveData<T> {
return Transformations.switchMap(
multiMap(liveDataSources, waitFirstValues) {
transform(it)
}) {
it
}
}
}
}
用法:
需要注意的是工作逻辑略有不同。导致更新的LiveData(signalledLiveData)作为参数传递给转换监听器,而不是所有LiveData的值。您可以通过value属性以通常的方式自己获取当前的LiveData值。
示例:
class SequenceLiveData(
scope: CoroutineScope,
start: Int,
step: Int,
times: Int
): LiveData<Int>(start) {
private var current = start
init {
scope.launch {
repeat (times) {
value = current
current += step
delay(1000)
}
}
}
}
suspend fun testMultiMap(lifecycleOwner: LifecycleOwner, scope: CoroutineScope) {
val liveS = MutableLiveData<String>("aaa")
val liveI = MutableLiveData<Int>()
val liveB = MutableLiveData<Boolean>()
val multiLiveWait: LiveData<String> = MyTransformations.multiMap(arrayOf(liveS, liveI, liveB)) {
when (it) {
liveS -> log("liveS changed")
liveI -> log("liveI changed")
liveB -> log("liveB changed")
}
"multiLiveWait: S = ${liveS.value}, I = ${liveI.value}, B = ${liveB.value}"
}
val multiLiveNoWait: LiveData<String> = MyTransformations.multiMap(arrayOf(liveS, liveI, liveB), false) {
when (it) {
liveS -> log("liveS changed")
liveI -> log("liveI changed")
liveB -> log("liveB changed")
}
"multiLiveNoWait: S = ${liveS.value}, I = ${liveI.value}, B = ${liveB.value}"
}
multiLiveWait.observe(lifecycleOwner) {
log(it)
}
multiLiveNoWait.observe(lifecycleOwner) {
log(it)
}
scope.launch {
delay(1000)
liveS.value = "bbb"
delay(1000)
liveI.value = 2222
delay(1000)
liveB.value = true
delay(1000)
liveI.value = 3333
}
}
suspend fun testMultiMapSwitch(lifecycleOwner: LifecycleOwner, scope: CoroutineScope) {
scope.launch {
val start1 = MutableLiveData(0)
val step1 = MutableLiveData(1)
val multiLiveData = MyTransformations.multiSwitch(arrayOf(start1, step1)) {
SequenceLiveData(scope, start1.value!!, step1.value!!, 5)
}
multiLiveData.observe(lifecycleOwner) {
log("$it")
}
delay(7000)
start1.value = 100
step1.value = 2
delay(7000)
start1.value = 200
step1.value = 3
delay(7000)
}
}
code
和nbDays
的单独 setter 可以触发两次 switchMap 吗?那么,观察者可能会接收到两个连续的值。如果你不想要这种行为,最好在MutableLiveData
中只使用一个Pair
对象来组合这两个值,就像下面 OP 的答案中所示。 - jL4