最近刚学安卓开发,目前在使用mvvm模式进行开发。我发现LiveData有一个很有意思的属性:
(https://developer.android.com/topic/libraries/architecture/livedata?hl=zh-cn)
通常,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。此行为的一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。
这给我造成了些麻烦。比如说,当我返回上一个fragment时, 这个fragment会马上运行observe(),观察到的数据则是上一次运行得到的数据。我在观察方程里有关于view组建的reference。当观察方程立刻被触发时,有时候这些组件还没initialize完毕而造成npe。
我自己的例子是这样的:我有一个登陆的界面,用户按下登陆键view就会把记录下的用户名和密码(用一个searchEvent的类表示)传给viewmodel, viewmodel会把用Transformations.switchMap()来调用repository里的retrofit API同时view里有一个观察这个活动的方程。点击登陆登陆成功后,在那个fragment(我称之为fragment B)如果我按返回返回这个登陆界面,他会马上登陆成功,再次回到fragmentB里
请问该如何防止这种情况呢?
这是我viewmodel的代码:
public class LoginViewModel extends BaseViewModel<LoginRepository> {
private final MutableLiveData<LoginEvent> loginEventMutableLiveData = new MutableLiveData<>();
private final LiveData<RemoteResponse<UserInfo>> remoteResponseMutableLiveData = Transformations
.switchMap(loginEventMutableLiveData, repository::login);
private final MutableLiveData<String> errMsgMutableLiveData = new MutableLiveData<>();
LoginViewModel(LoginRepository baseRepository) {
super(baseRepository);
}
public LiveData<RemoteResponse<UserInfo>> getRemoteResponseMutableLiveData() {
return remoteResponseMutableLiveData;
}
public MutableLiveData<String> getErrMsgMutableLiveData() {
return errMsgMutableLiveData;
}
public void login(LoginEvent loginEvent) {
if (Utils.isNullOrEmpty(loginEvent.userId)) {
errMsgMutableLiveData.setValue("Please enter a valid password!");
return;
}
if (Utils.isNullOrEmpty(loginEvent.password)) {
errMsgMutableLiveData.setValue("Please enter a valid password!");
return;
}
loginEventMutableLiveData.setValue(loginEvent);
}
}
这是我loginfragment的代码:
public class LoginFragment extends BaseFragment<LoginViewModel, LoginRepository> {
private LoginFragmentBinding binding;
private NavigationManager navigationManager;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = LoginFragmentBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
navigationManager = (NavigationManager) context;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
binding.btnLogin.setOnClickListener( v -> {
viewModel.login(new LoginEvent(binding.etUserIdLogin.getText().toString(),
Utils.md5Encryption(binding.etPasswordLogin.getText().toString()))); // faker user info
});
viewModel.getRemoteResponseMutableLiveData().observe(getViewLifecycleOwner(), it -> {
if (it != null && it.status.equals("OK")) {
Utils.constructToast(getContext(), "Login success!").show();
Config.userId = it.response.userId;
Config.name = it.response.name;
navigationManager.navigateTo(new HomeListFragment());
} else {
Utils.constructToast(getContext(), it == null ? "Error !" : it.status).show();
}
});
viewModel.getErrMsgMutableLiveData().observe(getViewLifecycleOwner(), it -> {
Utils.constructToast(getContext(), it).show();
});
}
@Override
protected LoginViewModel getViewModel() {
return new ViewModelProvider(requireActivity(), getFactory()).get(LoginViewModel.class);
}
@Override
protected ViewModelProvider.Factory getFactory() {
return new ViewModelProvider.Factory() {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new LoginViewModel(getRepository());
}
};
}
@Override
protected LoginRepository getRepository() {
return new LoginRepository();
}
}
补充:我寻思Transformation.distinctUntilChange()可能有用,但是用了好像没效果,不知道是不是这么用
private final LiveData<RemoteResponse<UserInfo>> remoteResponseMutableLiveData = Transformations.distinctUntilChange(Transformations
.switchMap(loginEventMutableLiveData, repository::login)));
我听说把viewmodel的东西手动清空也可以,但不知道怎么清空