我在我的应用程序上使用MVVM + LiveData + Dagger 2.11。在SignInFragment上,单击textview将请求发送到服务器,并在小吃栏上显示respopnse。第一次单击textview时,它可以正常工作。如果再次单击,它会发送请求(在此处显示点心栏响应消息)和ViewModel MediatorLiveData观察者onChanged方法(称为muliple times)。这是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);
}
最佳答案
似乎您每次单击addSource
时都在呼叫带有不同电话号码的不同LiveData
的resend_activation_code_textview
。这些不同的LiveData
源也都与称为NetworkResponseObservers
的不同setValue()
相关联。 setValue()
是什么会更新您的听力片段,这就是所谓的太多次了。
我认为问题是因为每次单击resend_activation_code_textview时都调用addSource
,并且从不删除任何源。
如果单击10次,则您的resend_activation_code_textview
将有10种不同的来源,而您可能只想使用一种。
添加源后,它会触发mediatorLiveData
的初始触发,因此您将始终至少触发一次mediatorLiveData
。当10个添加的源中的任何一个更新时,它也会更新您的setValue()
,并调用mediatorLiveData
。根据setValue()
的操作以及是否更新其他10个LiveData源中的任何一个,这将触发一次单击的多个signInRepository.resendActivationCode
调用。
您可以调用一个removeSource()方法,以确保您一次不会拥有多个源,这可能会消除多个onChanged调用。但是对于我想您要尝试执行的操作,有一个内置的解决方案(它在后台使用MediatorLiveData)-是switchMap Transformation。setValue()
允许您更改switchMap
正在侦听的基础源,而无需更新观察者。因此,您无需单击LiveData
10次并添加10个不同的源,而是每次单击resend_activation_code_textview
时,以前的源将被替换为新的源。resend_activation_code_textview
的示例方案是您有一种查找switchMap
的方法。您制作一个普通的LiveData来存储用户ID,然后使用userById()
转换,以便为当前用户使用另一个LiveData。随着id的更改,当前用户被换出并更新:
MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id));
void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}
我认为您正在使用电话号码和国家/地区代码进行类似操作。这就像您的“身份证”。您将要创建一个包含电话号码和国家/地区代码的对象,我们将其称为
switchMap
。然后,您将创建一个FullPhoneNumber
,与上一个示例中的LiveData<FullPhoneNumber> phoneNumberLiveData
相似。然后:LiveData<ResendActivationCodeResponse> reactivationLiveData =
Transformations.switchMap(phoneNumberLiveData, currentPhoneNumber ->
signInRepository.resendActivationCode(currentPhoneNumber.getNumber(), currentPhoneNumber.getCountryCode());
希望对您有所帮助或至少为您指明正确的方向!