MVVM MediatorLiveData观察者onchanged多次调用

时间:2017-10-26 18:27:33

标签: java android mvvm android-architecture-components android-livedata

我在我的应用程序上使用MVVM + LiveData + Dagger 2.11。在SignInFragment上单击textview发送请求到服务器并在快餐栏上显示respopnse。它在第一次点击textview时工作正常。如果我再次点击,它会发送请求(在此处显示snackbar响应消息)和ViewModel MediatorLiveData观察者onChanged方法称为muliple次。它是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);
    }

1 个答案:

答案 0 :(得分:3)

每次点击addSource时,您似乎正在呼叫LiveData,其中resend_activation_code_textview与不同的电话号码相关联。这些不同的LiveData来源也与不同的NetworkResponseObservers相关联,即调用setValue()setValue()更新了您的侦听片段以及被称为太多次的内容。

我认为问题是因为每次点击resend_activation_code_textview时都会调用addSource,而且您永远不会删除任何来源。

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

添加来源后,它会对mediatorLiveData执行初始触发,因此您始终会至少触发一次setValue()。如果更新了10个添加的来源中的任何一个,它还会更新您的mediatorLiveData,并致电setValue()。取决于signInRepository.resendActivationCode执行的操作以及是否更新任何其他10个LiveData源,这将触发多次setValue()次调用。

您可以调用removeSource()方法确保一次不会有多个来源,这可能会消除多个onChanged调用。但是,内置的解决方案适用于我认为您尝试做的事情(使用MediatorLiveData) - 它是 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);
 }

我认为您正在使用电话号码和国家/地区代码执行类似的操作。这就像你的&#34; id&#34;。您要创建一个包含电话号码和国家/地区代码的对象,我们可以将其称为FullPhoneNumber。然后,您将制作一个LiveData<FullPhoneNumber> phoneNumberLiveData,类似于上一个示例中的userIdLiveData。然后:

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

希望有助于或至少指出正确的方向!