如何正确组合两个实时数据对象

How to make a combination of two live data objects properly

我有两个相同类型的 LiveData 对象,我想对输出进行相同的计算。类似于 Rx 中的合并运算符,我该如何实现。目前我是这样做的:

class VM : ViewModel() {
    val input1LiveData = MutableLiveData<Int>()
    val input2LiveData = MutableLiveData<Int>()
    val squareLiveData = MutableLiveData<Int>()
}

class MyFragment : Fragment() {
    private void fillSquare(input: Int){
        viewmodel.squareLiveData.value = input * input
    }
    override onViewCreated(){
        viewmodel.input1LiveData.observe(viewLifecycleOwner){
            fillSquare(it * it)
        }
        viewmodel.input2LiveData.observe(viewLifecycleOwner){
            fillSquare(it * it)
        }
    }
}

我认为这是一个糟糕的方法

您可以使用 MediatorLiveData,这是一种特殊类型的 LiveData,能够连接到其他 liveData 对象作为源。您可以像这样改进上面的代码:

class VM : ViewModel() {
    val input1LiveData = MutableLiveData<Int>()
    val input2LiveData = MutableLiveData<Int>()
    
    val squareLiveData = MediatorLiveData<Int>().apply{
        val observer = Observer<T> { value = it*it }
        addSource(input1LiveData, observer)
        addSource(input2LiveData, observer)
    }
}

class MyFragment : Fragment() {
    override onViewCreated(){
        viewmodel.squareLiveData.observe(viewLifecycleOwner){
            //do your ui updates
        }
    }
}

以上代码将创建一个 MediatorLiveData 具有 2 个源的对象,观察者将计算平方并设置值。


您也可以使用 ReactiveLiveData 库,以响应式方法实现此功能,使代码更易于编写和阅读:D 就像这样。

class VM : ViewModel() {
    val input1LiveData = MutableLiveData<Int>()
    val input2LiveData = MutableLiveData<Int>()

    val squareLiveData = input1LiveData.merge(input2LiveData).map{it*it}
}

class MyFragment : Fragment() {
    override onViewCreated(){
        viewmodel.squareLiveData.observe(viewLifecycleOwner){
            //do your ui updates
        }
    }
}

提供的答案是正确的,确实是一个MediatorLiveData可以解决这个问题,例如:

public class CombinedLiveData2<A, B> extends MediatorLiveData<Pair<A, B>> {
    private A a;
    private B b;

    public CombinedLiveData2(LiveData<A> ld1, LiveData<B> ld2) {
        setValue(Pair.create(a, b));

        addSource(ld1, (a) -> { 
             if(a != null) {
                this.a = a;
             } 
             setValue(Pair.create(a, b)); 
        });

        addSource(ld2, (b) -> { 
            if(b != null) {
                this.b = b;
            } 
            setValue(Pair.create(a, b));
        });
    }
}

如果您想对更多 LiveData 执行此操作,可以使用 https://github.com/Zhuinden/livedata-combinetuple-kt/

class VM : ViewModel() {
    val input1LiveData = MutableLiveData<Int>(0)
    val input2LiveData = MutableLiveData<Int>(0)
    val squareLiveData = combineTupleNonNull(input1LiveData, input2LiveData)
                             .map { (input1, input2) -> 
                                 /* do transform */
                             }
}

class MyFragment : Fragment() {
    override onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)    

        viewmodel.squareLiveData.observe(viewLifecycleOwner) {
            // do something with square value
        }
    }
}