2

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

1 回答 1

4

每次单击您时,您似乎都在addSource使用与不同电话号码相关联的不同电话号码进行呼叫。这些不同的来源也都关联着不同的,即调用。是什么更新了你的听力片段,什么是被称为太多次的东西。LiveDataresend_activation_code_textviewLiveDataNetworkResponseObserverssetValue()setValue()

我相信这个问题是因为你addSource每次点击 resend_activation_code_textview 时都会打电话,而且你永远不会删除任何来源。

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

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


有一个removeSource()方法,您可以调用它来确保您一次不会有多个源,这可能会摆脱多个 onChanged 调用。但是对于我认为您正在尝试做的事情,有一个内置的解决方案(它在后台使用 MediatorLiveData)——它是switchMap Transformation

switchMap允许您更改 aLiveData正在侦听的底层源,而无需更新观察者。因此,与其单击resend_activation_code_textview10 次并添加 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);
 }

我认为您正在使用电话号码和国家/地区代码做类似的事情。这就像你的“身份”。您需要创建一个包含电话号码和国家代码的对象,我们称之为FullPhoneNumber。然后您将制作一个LiveData<FullPhoneNumber> phoneNumberLiveData,这与userIdLiveData前面示例中的类似。然后:

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

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

于 2017-10-27T19:00:10.450 回答