Преобразования Общая карта множественных переключателей, возможно ли это? - PullRequest
0 голосов
/ 15 апреля 2019

Концепция 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 триггеров

...