转换通用多重开关图,这可能吗?

时间:2019-04-15 17:03:37

标签: android generics kotlin transformation switchmap

“转换” switchMap的概念对我来说仍然很棘手,因此我试图将其解开。 我了解,如果我有一个用Transformations.switchMap初始化的LiveDataA,它观察到LiveDataB,则当B发生变化时,它将用新MediatorLiveDataC替换LiveDataA,该MediatorLiveDataC包含附加的旧LiveDataA观察器,以便上层观察它的类可以继续观察

因此,基本上,如果我有一个提供程序LiveDataA,它包含DB或Service等内容,则很有帮助,当它连接时,将触发switchMap并激活switchMap函数以返回LiveDataC,代码如下: / p>

观察者文件(可能是通过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 }

因此,即使服务可能需要一些时间才能将服务绑定发送到存储库,此代码也会在服务连接时更新UI中的用户。

如果要观察多个变量,就会发生什么事情:也许我想让选定类型和区域的最后一个用户使用,但是类型和区域也可以随时更改,因此我已经解决了这一问题,不是那么通用。

回购:

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 }

定制的switchMap:

@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个触发器仍然存在问题

0 个答案:

没有答案