androidfirebase-realtime-databaseandroid-livedatamediatorlivedata

Merging different depending LiveData objects into one


Here is my problem. I have to generate data from three different LiveData objects into one object. Two of them depending of one of them. The observer should only be triggered if all three object data was received.

Here is an example:

The resulting object contains information of a product and a specific review rating of this product. The result object looks something like this:

public class RatedProduct {
    private String productKey; // database key to the product
    private String ratingKey; // database key to the specific rating
    private Product product; // the product information
    private Rating rating; // the specifiv rating information
    private HashMap<String, Boolean> customTags; // list of tags
...
}

The sources distributed over different databases locations.

The first object UserRating contains additional data (customTags) and only two keys, referencing to the product and to the rating information.

public class UserRating {
    private String ratingKey;
    private String productKey;
    private HashMap<String, Boolean> customTags;
...
}

The Rating and Product objects holding the specific information.

public class Rating {
    private float value; // value of the rating
    private String productKey; // database key to the product
    private String ratingComment;
...
}
public class Product {
    private String name; // name of the product
    private HashMap<String, Boolean> ratings; // list of all ratings
...
}

The task is to gather all the data from UserRating, Rating and Product into one RatedProduct but hiding the necessary steps.

I think the best way is to use three different LiveData objects and merging them with the help of a MediatorLiveData

But I'm not sure how to do this in the right manner. I'm a little bit stucked. I know how to add the UserRatingLiveData as source to the MediatorLiveData but I'm lost how to add the remaining two, call them RatingLiveData and ProductLiveData with respect to the keys contained in UserRatingLiveData.

The second problem is, that MediatorLiveData<RatedProduct> should only update if all data was retrieved and not if only one of them.

That's what I have (more or less nothing). The interessting part is LiveData<RatedProduct> getRatedProductLiveData() I guess:

public class UserViewModel extends ViewModel {
    ...
    private UserRatingsLiveData mUserRatingsLiveDate;

    public UserViewModel() {

        //
        // Getting current user
        FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();

        if (mUser != null) {
            this.mUserRatingsLiveDate =
                    new UserRatingsLiveData(
                            mUsersDatabaseReference.child(mUser.getUid() + "/" + DB_PATH_RATINGS));
        } else {
            Log.d(LOG_TAG, "mUser is NULL");
        }
    }

    @NonNull
    public LiveData<RatedProduct> getRatedProductLiveData() {
        final MediatorLiveData<RatedProduct>  liveDataMerger = new MediatorLiveData<> ();
        final UserRating receivedUserRating = new UserRating();


        liveDataMerger.addSource(this.mUserRatingsLiveDate, new Observer<UserRating>() {
            public void onChanged(UserRating userRatings) {

            }
        });
        return liveDataMerger;
    }
}

As an additional hint: The data base is a Firebase RealTimeDatabase, if this could help.


Solution

  • Try like that:

    
    class MyViewModel {
        val mainLiveData: LiveData<CustomClass> get() = _mainLiveData
    
        private val firstLiveData = MutableLiveData<String>()
        private val secondLiveData : MutableLiveData<Int>? = null
        private val thirdLiveData : MutableLiveData<Long>? = null
    
        private val _mainLiveData = MediatorLiveData<CustomClass>().also { mergeLiveData ->
            mergeLiveData.addSource(firstLiveData) {
               if (secondLiveData != null ) mergeLiveData.removeSource(secondLiveData)
               if (thirdLiveData != null) mergeLiveData.removeSource(thirdLiveData)
    
               secondLiveData = getSecondLiveData(firstLiveData.value)
               thirdLiveData = getThirdLiveData(firstLiveData.value)
    
               mergeLiveData.addSource(secondLiveData) {
                   updateMainLiveData(mergeLiveData)
               }
               
               mergeLiveData.addSource(thirdLiveData) {
                   updateMainLiveData(mergeLiveData)
               }
            }
        }
    
        private fun updateMainLiveData(mainLiveData: MediatorLiveData<CustomClass>) {
            val first = firstLiveData.value ?: return
            val second = secondLiveData.value ?: return
            val third = thirdLiveData.value ?: return
    
            mainLiveData.value = CustomClass(first, second, third)
        }
    
        data class CustomClass(
            val first: String,
            val second: Int,
            val third: Long
        )
    }