多次调用 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());
希望对您有所帮助或至少为您指明正确的方向!
我在 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());
希望对您有所帮助或至少为您指明正确的方向!