Как объединить два живых данных один за другим? - PullRequest
0 голосов
/ 30 мая 2018

У меня следующий вариант использования: пользователь заходит на регистрационную форму, вводит имя, адрес электронной почты и пароль и нажимает кнопку регистрации.После этого система должна проверить, принято ли электронное письмо или нет, и на основании этого сообщения об ошибке показа или создать нового пользователя ...

Я пытаюсь сделать это, используя Room, ViewModel и LiveData.Это какой-то проект, на котором я пытаюсь изучить эти компоненты, и у меня нет удаленного API, я буду хранить все в локальной базе данных

Итак, у меня есть эти классы:

  • RegisterActivity
  • RegisterViewModel
  • Пользователь
  • UsersDAO
  • UsersRepository
  • UsersRegistrationService

Так что идея, которую я имеюзаключается в том, что к кнопке регистрации будет присоединен слушатель, который будет вызывать метод RegisterViewModel::register().

class RegisterViewModel extends ViewModel {

    //...

    public void register() {
        validationErrorMessage.setValue(null);
        if(!validateInput())
            return;
        registrationService.performRegistration(name.get(), email.get(), password.get());
    }

    //...

}

Так что это основная идея, я также хочу, чтобы performRegistration вернул мне вновь созданного пользователя.

Больше всего меня беспокоит то, что я не знаю, как реализовать performRegistration функцию в сервисе

class UsersRegistrationService {
    private UsersRepository usersRepo;

    //...

    public LiveData<RegistrationResponse<Parent>>  performRegistration(String name, String email, String password) {
         // 1. check if email exists using repository
         // 2. if user exists return RegistrationResponse.error("Email is taken") 
         // 3. if user does not exists create new user and return RegistrationResponse(newUser)
    }
}

Как я понимаю, методы, которые в UsersRepository должны возвращать LiveDataпотому что UsersDAO возвращает LiveData

@Dao
abstract class UsersDAO { 
    @Query("SELECT * FROM users WHERE email = :email LIMIT 1")
    abstract LiveData<User> getUserByEmail(String email);
}

class UsersRepository {
    //...
    public LiveData<User> findUserByEmail(String email) {
        return this.usersDAO.getUserByEmail(email);
    }
}

Поэтому моя проблема заключается в том, как реализовать функцию performRegistration() и как передать значение обратно для просмотра модели, а затем как изменить активность с RegisterActivity на MainActivity ...

Ответы [ 5 ]

0 голосов
/ 31 июля 2019
LiveData liveData1 = ...;
 LiveData liveData2 = ...;

 MediatorLiveData liveDataMerger = new MediatorLiveData<>();
 liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
 liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
0 голосов
/ 17 июля 2019

Вы можете определить метод, который будет объединять несколько LiveDatas, используя 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));
        });
    }
}

Если вам нужно больше значений, тогда вы можете создать CombinedLiveData3<A,B,C> ивыставить Triple<A,B,C> вместо пары и т. д.

0 голосов
/ 12 июня 2019

Хосе Альсеррека, вероятно, лучший ответ на этот вопрос :

fun blogpostBoilerplateExample(newUser: String): LiveData<UserDataResult> {

    val liveData1 = userOnlineDataSource.getOnlineTime(newUser)
    val liveData2 = userCheckinsDataSource.getCheckins(newUser)

    val result = MediatorLiveData<UserDataResult>()

    result.addSource(liveData1) { value ->
        result.value = combineLatestData(liveData1, liveData2)
    }
    result.addSource(liveData2) { value ->
        result.value = combineLatestData(liveData1, liveData2)
    }
    return result
}
0 голосов
/ 17 июля 2019

Вы можете использовать мой вспомогательный метод:

val profile = MutableLiveData<ProfileData>()

val user = MutableLiveData<CurrentUser>()

val title = profile.combineWith(user) { profile, user ->
    "${profile.job} ${user.name}"
}

fun <T, K, R> LiveData<T>.combineWith(
    liveData: LiveData<K>,
    block: (T?, K?) -> R
): LiveData<R> {
    val result = MediatorLiveData<R>()
    result.addSource(this) {
        result.value = block.invoke(this.value, liveData.value)
    }
    result.addSource(liveData) {
        result.value = block.invoke(this.value, liveData.value)
    }
    return result
}
0 голосов
/ 13 сентября 2018

С помощью MediatorLiveData вы можете комбинировать результаты из нескольких источников.Вот пример того, как я мог бы объединить два источника:

class CombinedLiveData<T, K, S>(source1: LiveData<T>, source2: LiveData<K>, private val combine: (data1: T?, data2: K?) -> S) : MediatorLiveData<S>() {

private var data1: T? = null
private var data2: K? = null

init {
    super.addSource(source1) {
        data1 = it
        value = combine(data1, data2)
    }
    super.addSource(source2) {
        data2 = it
        value = combine(data1, data2)
    }
}

override fun <T : Any?> addSource(source: LiveData<T>, onChanged: Observer<T>) {
    throw UnsupportedOperationException()
}

override fun <T : Any?> removeSource(toRemote: LiveData<T>) {
    throw UnsupportedOperationException()
}
}

Вот суть для выше, на случай, если он будет обновлен в будущем: https://gist.github.com/guness/0a96d80bc1fb969fa70a5448aa34c215

...