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