多次调用 MVVM MediatorLiveData 观察器 onchanged

MVVM MediatorLiveData observer onchanged called multiple times

我在 app.On SignInFragment 上使用 MVVM + LiveData + Dagger 2.11 单击文本视图向服务器发送请求并在 snackbar 上显示响应。它在第一次单击 textview.If 时工作正常,我再次单击,它发送请求(在此处显示 snackbar 响应消息)和 ViewModel MediatorLiveData 观察器 onChanged 方法调用多个 times.Is 它是 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);
    }

每次点击 resend_activation_code_textview 时,您似乎都在用与不同 phone 号码关联的不同 LiveData 呼叫 addSource。这些不同的 LiveData 源也都与调用 setValue() 的不同 NetworkResponseObservers 相关联。 setValue() 是什么更新了你的听力片段,什么是叫了太多次。

我认为问题是因为您每次单击 resend_activation_code_textview 时都会调用 addSource,并且您从未删除任何来源。

如果您点击 resend_activation_code_textview 10 次,您的 mediatorLiveData 将有 10 个不同的来源,而您可能只想要一个。

添加源后,它会初始触发您的 mediatorLiveData,因此您将始终至少触发一次 setValue()。当更新 10 个添加的源中的任何一个时,它也会更新您的 mediatorLiveData,并调用 setValue()。根据 signInRepository.resendActivationCode 的作用以及它是否更新了其他 10 个 LiveData 源中的任何一个,这将触发多次 setValue() 一次单击调用。


有个removeSource() method which you could call to make sure you never have more than one source at a time, this likely getting rid of the multiple onChanged calls. But there's a built in solution for what I think you're trying to do (which uses MediatorLiveData under the hood) -- it's the switchMap Transformation.

switchMap 允许您更改 LiveData 正在侦听的基础源,而无需更新观察者。因此,与其单击 resend_activation_code_textview 10 次并添加 10 个不同的来源,您可以拥有它,以便每次单击 resend_activation_code_textview 之前的来源都将 交换 一个新来源。

switchMap 的示例场景是您有一个查找 userById() 的方法。您制作一个普通的 LiveData 来存储用户 ID,然后使用 switchMap 转换,以便您拥有当前用户的另一个 LiveData。随着id的变化,当前用户被换出并更新:

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

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

我认为您正在对 phone 号码和国家/地区代码做类似的事情。这就像你的 "id"。您需要创建一个包含 phone 号码和国家/地区代码的对象,我们称它为 FullPhoneNumber。然后你将制作一个 LiveData<FullPhoneNumber> phoneNumberLiveData,它类似于前面示例中的 userIdLiveData。那么:

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

希望对您有所帮助或至少为您指明正确的方向!