Наблюдатель MVVM MediatorLiveData onchanged вызывается несколько раз

Я использую MVVM + LiveData + Dagger 2.11 в своем приложении. В SignInFragment щелкните запрос на отправку текстового представления на сервер и покажите ответ на панели закусок. Он отлично работает при первом щелчке по текстовому изображению. Если я нажимаю еще раз, он отправляет запрос (здесь отображается сообщение с ответом на снэк-бар) и метод onChanged наблюдателя ViewModel MediatorLiveData, называемый множественным разом. Является ли это поведение MediatorLiveData по умолчанию?

SignInViewModel.java

public class SignInViewModel extends AndroidViewModel {

    @Inject
    MediatorLiveData mediatorLiveData;

    @Inject
    SnackbarMessage mSnackbarTextLiveData = new SnackbarMessage();

    @Inject
    public SignInViewModel(Application application,SignInRepository signInRepository) {
        super(application);
        this.signInRepository = signInRepository;
    }

    public MediatorLiveData<ResendActivationCodeResponse> resendActivationCode(final String phoneNumber, final String countryCode) {
        final MutableLiveData<NetworkResponse> connectViaPhoneResponseMutableLiveData = signInRepository.resendActivationCode(phoneNumber, countryCode);

        mediatorLiveData.addSource(connectViaPhoneResponseMutableLiveData, new NetworkResponseObserver() {
            @Override
            public void onSuccess(Object data) {
                mediatorLiveData.setValue(data);
            }

            @Override
            public void onBadRequest(Object data, String errorMessage) {
                mSnackbarTextLiveData.setValue(errorMessage);
            }

            @Override
            public void onUnAuthorisedError(Object data) {
                mSnackbarTextLiveData.setValue(data.toString());
            }

            @Override
            public void onFailure(Object data, String errorMessage) {
                mSnackbarTextLiveData.setValue(errorMessage);
            }

            @Override
            public void onNoNetworkAvailable(Object data, String errorMessage) {
                mSnackbarTextLiveData.setValue(data.toString());
            }

            @Override
            public void onLoading(Object data) {

            }
        });
        return mediatorLiveData;
    }
}

SignInFragment.java

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mSignInViewModel = ViewModelProviders.of(mActivity, mViewModelFactory).get(SignInViewModel.class);
        setupSnackbar();
    }

    private void setupSnackbar() {
        mSignInViewModel.getSnackbarMessage().observe(this, new SnackbarMessage.SnackbarObserver() {
            @Override
            public void onNewMessage(String snackbarMessage) {
                ActivityUtils.showSnackbar(getView(), snackbarMessage);
            }
        });
    }

    @OnClick(R.id.resend_activation_code_textview)
    public void reSendActivationCode() {
        showProgress(true);

        final MediatorLiveData<ResendActivationCodeResponse> resendActivationCodeResponseMediatorLiveData = mSignInViewModel.resendActivationCode(mPhoneNumber, mCountryCode);


        Observer<ResendActivationCodeResponse> resendActivationCodeResponseObserver = new Observer<ResendActivationCodeResponse>() {

            @Override
            public void onChanged(@Nullable ResendActivationCodeResponse resendActivationCodeResponse) {
                if (resendActivationCodeResponse != null) {
                    showProgress(false);
                    ActivityUtils.showSnackbar(getView(), activationCodeResentMessage);
                    //resendActivationCodeResponseMediatorLiveData.removeObserver(this);
                }
            }
        };


        resendActivationCodeResponseMediatorLiveData.observe(PhoneNumberActivationFragment.this, resendActivationCodeResponseObserver);
    }

person Ramprasad    schedule 26.10.2017    source источник
comment
Не могли бы вы помочь мне с: stackoverflow.com/questions/60078980/   -  person    schedule 05.02.2020


Ответы (1)


Похоже, вы звоните addSource с разных LiveData, связанных с разными телефонными номерами, каждый раз, когда нажимаете на ваш resend_activation_code_textview. Эти разные LiveData источники также все связаны с разными NetworkResponseObservers, которые вызывают setValue(). setValue() - это то, что обновляет ваши прослушиваемые фрагменты, и то, что вызывается слишком много раз.

Я считаю, что проблема в том, что вы вызываете addSource каждый раз, когда нажимаете Resend_activation_code_textview, и никогда не удаляете источники.

Если вы нажмете resend_activation_code_textview 10 раз, ваш mediatorLiveData будет иметь 10 разных источников, тогда как вам, вероятно, нужен только один.

Когда добавляется источник, он запускает начальный запуск вашего mediatorLiveData, поэтому вы всегда будете запускать setValue() хотя бы один раз. При обновлении любого из 10 добавленных источников он также обновит ваш mediatorLiveData и вызовет setValue(). В зависимости от того, что signInRepository.resendActivationCode делает, и если он обновляет какой-либо из других 10 источников LiveData, это вызовет несколько setValue() вызовов для одного щелчка.


Там есть removeSource () метод, который вы можете вызвать, чтобы убедиться, что у вас никогда не будет более одного источника одновременно, что, вероятно, избавит от множества вызовов onChanged. Но есть встроенное решение для того, что, я думаю, вы пытаетесь сделать (которое использует MediatorLiveData под капотом) - это преобразование switchMap.

switchMap позволяет вам изменить базовый источник, который LiveData слушает, без обновления наблюдателей. Таким образом, вместо того, чтобы нажимать resend_activation_code_textview 10 раз и добавлять 10 разных источников, вы можете настроить его так, чтобы каждый раз, когда вы нажимаете resend_activation_code_textview предыдущий источник, был заменен на новый источник.

В примере сценария для switchMap у вас есть метод поиска userById(). Вы создаете обычные LiveData для хранения идентификатора пользователя, а затем используете преобразование switchMap, чтобы у вас были другие LiveData для текущего пользователя. При изменении идентификатора текущий пользователь заменяется и обновляется:

MutableLiveData userIdLiveData = ...;
 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     repository.getUserById(id));

 void setUserId(String userId) {
      this.userIdLiveData.setValue(userId);
 }

Думаю, вы делаете нечто подобное с номером телефона и кодом страны. Это похоже на ваш "id". Вам нужно создать объект, содержащий номер телефона и код страны, назовем его FullPhoneNumber. Затем вы создадите LiveData<FullPhoneNumber> phoneNumberLiveData, который похож на userIdLiveData в предыдущем примере. Потом:

LiveData<ResendActivationCodeResponse> reactivationLiveData = 
Transformations.switchMap(phoneNumberLiveData, currentPhoneNumber ->
     signInRepository.resendActivationCode(currentPhoneNumber.getNumber(), currentPhoneNumber.getCountryCode());

Надеюсь, это поможет или, по крайней мере, укажет вам правильное направление!

person Lyla    schedule 27.10.2017
comment
У меня есть случай использования, когда наблюдатель не запускает (обновляет) с первого раза: stackoverflow.com/questions/57650511/ Может ли что-нибудь из приведенного выше решения помочь в моем случае? Буду признателен за любые мысли, которые вы могли бы предложить ... - person AJW; 26.08.2019
comment
Не могли бы вы помочь мне с: stackoverflow.com/questions/60078980/ - person ; 05.02.2020
comment
Эта проблема испортила мне 2 часа жизни, а потом я наткнулся на этот ответ. Пальцы вверх! - person Shahood ul Hassan; 19.02.2020