Концепция Transformations switchMap для меня все еще непроста, поэтому я пытаюсь распутать ее в своем уме.Я понимаю, что если у меня есть LiveDataA, который инициализируется с помощью Transformations.switchMap, который наблюдает за LiveDataB, то при изменении B он заменит LiveDataA новым MediatorLiveDataC, содержащим старые прикрепленные наблюдатели LiveDataA, так что старшие классы, наблюдающие за ним, могут продолжатьсянаблюдение
В общем, полезно, если у меня есть провайдер LiveDataA, который содержит что-то вроде БД или службы и т. д., когда он подключается, срабатывает switchMap и активируется функция switchMap, возвращая LiveDataC,в коде:
Файл наблюдателя (возможно, Фрагмент через ViewModel)
myViewModel.getLastUserLD().observe(lifeCycleOwner, Observer<User> {
...add user name to some view...
}
Файл репо:
val myServiceMLD = MutableLiveData<MyService>()
var lastUserLD : LiveData<User>
init{
lastUserLD = Transforamtions.switchMap(myServiceMLD) {
//the service provides a liveData with the last user
it.getLastUserLD
}
}
fun getLastUserLD() = lastUserLD
//this occurs asynchronously, at a certain time it hooks, but we don't know when
setMyService(myService: MyService) { myServiceMLD.value = myService }
Таким образом, эти коды обновляют пользователя в пользовательском интерфейсе, когда службаподключается, даже если может потребоваться некоторое время, прежде чем служба будет привязана к репо.
Дело в том, что произойдет, если у меня будет несколько переменных для наблюдения: возможно, я хочу получить последнего пользователявыбранный тип и регион, но тип и регион также могут изменитьсяВ любое время я решил его таким способом, который не является настолько общим.
Репо:
val myServiceMLD = MutableLiveData<MyService>()
val userTypeMLD = MutableLiveData<Int>()
val userRegionMLD = MutableLiveData<String>()
var lastUserLD : LiveData<User>
init{
lastUserLD = Transforamtions.multipleSwitchMap(myServiceMLD, userTypeMLD, userRegionMLD) { s, ut, ur ->
//the service provides a liveData with the last user
s.getLastUserLD(ut, ur)
}
}
fun getLastUserLD() = lastUserLD
//this occurs asynchronously, at a certain time it hooks, but we don't
know when
setMyService(myService: MyService) { myServiceMLD.value = myService }
setUserType(type: Int) { userTypeMLD.value = type }
setUserRegion(region: String) { userRegionMLD.value = region }
Настраиваемая карта-свитч:
@MainThread
fun <W, X, Y, Z> multipleSwitchMap(source1: LiveData<W>, source2: LiveData<X>, source3: LiveData<Y>,
switchMapFunction: (W, X, Y) -> LiveData<Z>): LiveData<Z> {
return MediatorLiveData<Z>().apply {
addSource(source1, object : Observer<W> {
var mSource: LiveData<Z>? = null
override fun onChanged(w: W) {
source2.value?.let { x ->
source3.value?.let { y ->
switchMapFunction.invoke(w, x, y).let { newLiveData ->
if (mSource === newLiveData) return
mSource?.let { removeSource(it) }
mSource = newLiveData
mSource?.let { addSource(it) { z -> value = z } }
}
}
}
}
})
addSource(source2, object : Observer<X> {
var mSource: LiveData<Z>? = null
override fun onChanged(x: X) {
source1.value?.let { w ->
source3.value?.let { y ->
switchMapFunction.invoke(w, x, y).let { newLiveData ->
if (mSource === newLiveData) return
mSource?.let { removeSource(it) }
mSource = newLiveData
mSource?.let { addSource(it) { z -> value = z } }
}
}
}
}
})
addSource(source3, object : Observer<Y> {
var mSource: LiveData<Z>? = null
override fun onChanged(y: Y) {
source1.value?.let { w ->
source2.value?.let { x ->
switchMapFunction.invoke(w, x, y).let { newLiveData ->
if (mSource === newLiveData) return
mSource?.let { removeSource(it) }
mSource = newLiveData
mSource?.let { addSource(it) { z -> value = z } }
}
}
}
}
})
}
}
Это работает, нокак я уже говорил, я считаю, что это не так универсально.Итак, вопрос в том, как я могу сделать его универсальным, чтобы я мог использовать его с любым количеством источников?
Я пытался сделать это, но я не до конца понимаю, как работают дженерики, или если это возможносделать что-то полностью общее в этом случае.Вот моя первая попытка.Я застрял при попытке добавить функцию switchMap в репозиторий, чтобы увидеть, как он будет анализировать универсальные типы.
@MainThread
fun <Any, Z> multipleSwitchMapGeneric(switchMapFunction: (Array<out LiveData<Any>>) -> LiveData<Z>,
vararg sources: LiveData<Any>): LiveData<Z> {
return MediatorLiveData<Z>().apply {
for(source in sources) {
addSource(source, object : Observer<Any> {
var mSource: LiveData<Z>? = null
override fun onChanged(w: Any) {
switchMapFunction.invoke(sources).let { newLiveData ->
if (mSource === newLiveData) return
mSource?.let { removeSource(it) }
mSource = newLiveData
mSource?.let { addSource(it) { y -> value = y; } }
}
}
})
}
}
}
Не могли бы вы сказать мне, если это возможно? Если да, то как ?, если нет, то почему?и как я мог бы использовать его в своем преобразовании репо?
Update1: нашел лучший способ сделать это с 3 установленными значениями
class TripleTrigger <X,Y,Z>(source: Triple<LiveData<X>, LiveData<Y>, LiveData<Z>>) :
MediatorLiveData<Triple<X,Y,Z>>() {
init {
addSource(source.first) {
source.second.value?.let { y -> source.third.value?.let { z -> value = Triple(it, y, z) } }
}
addSource(source.second) {
source.first.value?.let { x -> source.third.value?.let { z -> value = Triple(x, it, z) } }
}
addSource(source.third) {
source.first.value?.let { x -> source.second.value?.let { y -> value = Triple(x, y, it) } }
}
}
}
в репо:
val myServiceMLD = MutableLiveData<MyService>()
val userTypeMLD = MutableLiveData<Int>()
val userRegionMLD = MutableLiveData<String>()
var lastUserLD : LiveData<User>
init{
lastUserLD = Transforamtions.switchMap(TripleTrigger(myServiceMLD, userTypeMLD, userRegionMLD)) {
//the service provides a liveData with the last user
it.first.getLastUserLD(it.second, it.third)
}
}
fun getLastUserLD() = lastUserLD
//this occurs asynchronously, at a certain time it hooks, but we don't
know when
setMyService(myService: MyService) { myServiceMLD.value = myService }
setUserType(type: Int) { userTypeMLD.value = type }
setUserRegion(region: String) { userRegionMLD.value = region }
По-прежнему возникают проблемы с этим для N триггеров