使用导航组件多次触发 LiveData 观察器
LiveData observer is being triggered multiple times using Navigation Component
场景:我有两个名为 FirstFragment
和 UnitFragment
的片段。我从 FirstFragment
到 UnitFragment
再到 select 使用 navController.popBackStack();
返回到 FirstFragmet
的单位并将单位数据发送到正在观察的 FirstFragment
单位数据.
这是我的 onViewCreated
of 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 观察器。
很遗憾,这不是您问题的答案:
I need a solution to ...
Avoid calling onViewCreated codes again.
Avoid triggering LiveData observer again.
我正在尝试解释导航及其行为或纠正一些误解。这些问题有不同的原因,Avoid calling onViewCreated codes again.
是一种迂回的方式。
I know onViewCreated will call again because Navigation Component replaces the fragments instead of adding them.
如您所知,在将片段添加到后台堆栈时替换片段,只需将旧片段从 fragmentManager
中分离出来。这意味着旧片段的 view 将被破坏。
并且会在您从后台堆栈中弹出 UnitFragment 时创建其视图。
所以不要在 onViewCreated
中调用任何 API 调用,因为它可能会调用多次(在配置更改、破坏片段等...)
最好使用 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
开始观察他们。此外,您应该在获得 navBackStackEntry 后使用 unit_data
。
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());
});
}
}
1. Avoid calling onViewCreated codes again
不,我认为您无法避免这种情况,因为当您导航到其他片段时,您的 FirstFragment 的视图会被破坏。所以回来后会重新创建call view
2. Avoid triggering LiveData observer again.
您可以像这样自定义实时数据:
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending: AtomicBoolean = AtomicBoolean(false)
override fun setValue(value: T) {
pending.set(true)
super.setValue(value)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(
owner,
Observer { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
)
}
}
设置新数据时通知观察者
场景:我有两个名为 FirstFragment
和 UnitFragment
的片段。我从 FirstFragment
到 UnitFragment
再到 select 使用 navController.popBackStack();
返回到 FirstFragmet
的单位并将单位数据发送到正在观察的 FirstFragment
单位数据.
这是我的 onViewCreated
of 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 观察器。
很遗憾,这不是您问题的答案:
I need a solution to ...
Avoid calling onViewCreated codes again.
Avoid triggering LiveData observer again.
我正在尝试解释导航及其行为或纠正一些误解。这些问题有不同的原因,Avoid calling onViewCreated codes again.
是一种迂回的方式。
I know onViewCreated will call again because Navigation Component replaces the fragments instead of adding them.
如您所知,在将片段添加到后台堆栈时替换片段,只需将旧片段从 fragmentManager
中分离出来。这意味着旧片段的 view 将被破坏。
并且会在您从后台堆栈中弹出 UnitFragment 时创建其视图。
所以不要在 onViewCreated
中调用任何 API 调用,因为它可能会调用多次(在配置更改、破坏片段等...)
最好使用 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
开始观察他们。此外,您应该在获得 navBackStackEntry 后使用 unit_data
。
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());
});
}
}
1. Avoid calling onViewCreated codes again
不,我认为您无法避免这种情况,因为当您导航到其他片段时,您的 FirstFragment 的视图会被破坏。所以回来后会重新创建call view
2. Avoid triggering LiveData observer again.
您可以像这样自定义实时数据:
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending: AtomicBoolean = AtomicBoolean(false)
override fun setValue(value: T) {
pending.set(true)
super.setValue(value)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(
owner,
Observer { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
)
}
}
设置新数据时通知观察者