user14533032
user14533032

Reputation:

How to make a combination of two live data objects properly

I have two LiveData objects of the same type and I want to do the same calculations on the output. Something like a merge operator in Rx how can I achieve this. currently I'm doing it like so:

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)
        }
    }
}

Which I guess is a bad approach

Upvotes: 2

Views: 871

Answers (2)

EpicPandaForce
EpicPandaForce

Reputation: 81549

The provided answer is correct, it is indeed a MediatorLiveData that can solve this issue, for example:

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));
        });
    }
}

If you want to do this for more LiveDatas, you can use 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
        }
    }
}

Upvotes: 1

Amin
Amin

Reputation: 3186

You can use a MediatorLiveData which is an special type of LiveData able to connect to other liveData objects as sources. You can improve the code above like this:

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
        }
    }
}

The above code will create a MediatorLiveData objects with 2 sources and the observer will calculate the square and will set in on value.


Also you can use ReactiveLiveData library, to implement this with a reactive approach which makes the code easier to write and read :D Just like this.

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
        }
    }
}

Upvotes: 4

Related Questions