我在我的应用程序上使用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);
}
答案 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());
希望有助于或至少指出正确的方向!