使用导航组件多次触发LiveData观察者
场景:我有两个名为FirstFragment和 的片段UnitFragment。我从FirstFragment到UnitFragment选择一个单元返回FirstFragmet使用navController.popBackStack();并将单元数据发送到FirstFragment观察单元数据的单元。
这是我onViewCreated的FirstFragment:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (viewModel == null) { // Lazy Initialization
ApiService apiService = ApiServiceProvider.getInstance();
AddNewWareViewModelFactory addNewWareViewModelFactory = new AddNewWareViewModelFactory(apiService);
viewModel = new ViewModelProvider(this, addNewWareViewModelFactory).get(AddWareViewModel.class);
}
Log.i(TAG, "OnViewCreated -----> Called");
viewModel.callNewWare(parentCode);
viewModel.getNewWareResponse().observe(getViewLifecycleOwner(),
resObject -> Log.i(TAG, "API Response LiveData Count -----> " + count++)); // Started From Zero
NavHostFragment navHostFragment = (NavHostFragment) requireActivity()
.getSupportFragmentManager()
.findFragmentById(R.id.container);
binding.button.setOnClickListener(v -> {
if (navHostFragment != null) {
NavController navController = navHostFragment.getNavController();
navController.navigate(FirstFragmentDirections.actionFirstFragmentToUnitFragment());
}
});
if (navHostFragment != null) {
NavController navController = navHostFragment.getNavController();
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
MutableLiveData<Unit> unitLiveData = savedStateHandle.getLiveData("unit_data");
unitLiveData.observe(getViewLifecycleOwner(), unit -> binding.tvUnit.setText(unit.getTitle()));
}
}
}
这是 LogCat 结果:
--- Go to FirstFragment for first time ---
I/FirstFragment: OnViewCreated -----> Called
I/FirstFragment: API Response LiveData Count -----> 0
--- Button clicked to go to UnitFragment to select a unit ---
I/UnitFragment: Selected Unit -----> Meter
--- Come back to FirstFragment ---
I/FirstFragment: OnViewCreated -----> Called
I/FirstFragment: API Response LiveData Count -----> 1
I/FirstFragment: API Response LiveData Count -----> 2
正如你在logcat的结果看,每次我点击按钮,转到UnitFragment,回来FirstFragment的onViewCreated会再次调用和API LiveDataObserver将被触发两次!
我知道 onViewCreated 会再次调用,因为导航组件会替换片段而不是添加它们。但我不知道为什么 LiveData 观察者会被触发两次。
我读了这篇文章,但他似乎忽略了导航组件。
我需要一个解决方案...
- 避免
onViewCreated再次调用代码。 - 避免再次触发 LiveData 观察者。
回答
不幸的是,这不是您问题的答案:
我需要一个解决方案...
避免再次调用 onViewCreated 代码。
避免再次触发 LiveData 观察者。
我试图解释导航及其行为或纠正一些误解。这些问题有不同的原因,Avoid calling onViewCreated codes again.是一种迂回的方式。
我知道 onViewCreated 会再次调用,因为导航组件会替换片段而不是添加它们。
如您所知,在将片段添加到后台堆栈时替换它们,只需将旧片段从fragmentManager. 这意味着旧片段的视图将被破坏。
当您从后台堆栈中弹出UnitFragment时,它将创建它的视图。
所以不要调用任何 API 调用,onViewCreated因为它可能会调用多次(在配置更改、销毁片段等...)
最好onCreate用于初始化非视图相关的组件(ViewModel + API 调用)。它减少了Lazy(!?) Initialization检查。
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ApiService apiService = ApiServiceProvider.getInstance();
AddNewWareViewModelFactory addNewWareViewModelFactory = new AddNewWareViewModelFactory(apiService);
viewModel = new ViewModelProvider(this /*owner*/, addNewWareViewModelFactory).get(AddWareViewModel.class);
viewModel.callNewWare(parentCode);
}
并开始观察它们onViewCreated。此外,您应该unit_data在获得 navBackStackEntry 后使用它。
if (navHostFragment != null) {
NavController navController = navHostFragment.getNavController();
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
MutableLiveData<Unit> unitLiveData = savedStateHandle.getLiveData("unit_data");
unitLiveData.observe(getViewLifecycleOwner(), unit -> {
savedStateHandle.remove("unit_data"); // add this line
return binding.tvUnit.setText(unit.getTitle());
});
}
}