Объединение разных зависящих объектов LiveData в один

#android #firebase-realtime-database #android-livedata #mediatorlivedata

#Android #firebase-база данных в реальном времени #android-livedata #mediatorlivedata

Вопрос:

Вот моя проблема. Мне нужно сгенерировать данные из трех разных LiveData объектов в один объект. Два из них зависят от одного из них. Наблюдатель должен запускаться только в том случае, если были получены все три объекта. Данные.

Вот пример:

Результирующий объект содержит информацию о продукте и конкретный рейтинг обзора этого продукта. Результирующий объект выглядит примерно так:

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

  

Источники распределены по разным базам данных.

Первый объект UserRating содержит дополнительные данные ( customTags ) и только два ключа, ссылающихся на продукт и на информацию о рейтинге.

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

Объекты Rating и Product содержат определенную информацию.

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

Задача состоит в том, чтобы собрать все данные из UserRating Rating и Product в один RatedProduct , но скрыть необходимые шаги.

Я думаю, что лучший способ — использовать три разных LiveData объекта и объединить их с помощью MediatorLiveData

Но я не уверен, как это сделать правильно. Я немного застрял. Я знаю, как добавить UserRatingLiveData источник as MediatorLiveData в, но я не понимаю, как добавить оставшиеся два, вызвать их RatingLiveData и ProductLiveData в отношении ключей, содержащихся UserRatingLiveData в.

Вторая проблема заключается в том, что MediatorLiveData<RatedProduct> обновление должно выполняться только в том случае, если были получены все данные, а не только один из них.

Это то, что у меня есть (более или менее ничего). Интересная часть LiveData<RatedProduct> getRatedProductLiveData() , я думаю:

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

В качестве дополнительной подсказки: база данных — это база данных Firebase RealTimeDatabase, если это может помочь.

Ответ №1:

Попробуйте так:

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

Комментарии:

1. Вау, это было быстро. Спасибо. Я попробую, но сначала мне нужен ускоренный курс по Kotlin: D Насколько я понимаю ваш подход, он будет работать, только если вы знаете все источники базы данных трех LiveData объектов одновременно. В моем случае я сначала firstLiveData не, потому что он содержит ключи secondLiveData и thirdLiveData . После этого я могу добавить их в качестве источника mergeLiveData . Я прав?

2. Какой тип mergeLiveData или это Kotlin, что для них нет определения?

3. В блоке also мы можем указать имя для этой переменной. mergeLiveData то есть _mainLiveData = MediatorLiveData<CustomClass>() , только для использования в also блоке, я даю такое имя.