[英]LiveData observer is being triggered multiple times using Navigation Component
場景:我有兩個名為FirstFragment
和UnitFragment
的片段。 我 go 從FirstFragment
到UnitFragment
到 select 一個單元返回到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 結果中看到的那樣,每次我單擊按鈕和 go 到UnitFragment
並返回到FirstFragment
時, onViewCreated
將再次調用,API LiveDataObserver 將被觸發兩次!!!
我知道 onViewCreated 會再次調用,因為導航組件替換了片段而不是添加它們。 但我不知道為什么 LiveData 觀察者會被觸發兩次。
我讀了這篇文章,但他似乎忽略了導航組件。
我需要一個解決方案...
onViewCreated
代碼。不幸的是,這不是您問題的答案:
我需要一個解決方案...
避免再次調用 onViewCreated 代碼。
避免再次觸發 LiveData 觀察者。
我試圖解釋導航及其行為或糾正一些誤解。 這些問題有不同的原因, Avoid calling onViewCreated codes again.
是一種迂回的方式。
我知道 onViewCreated 會再次調用,因為導航組件替換了片段而不是添加它們。
如您所知,在將片段添加到后台堆棧時替換片段,只需將舊片段從fragmentManager
管理器中分離出來。 這意味着舊片段的視圖將被破壞。
當您從后台堆棧中彈出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
中觀察它們。 此外,您應該在獲得時使用來自unit_data
的 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 視圖會破壞。 因此,您回來后將再次創建調用視圖。
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)
}
}
)
}
}
它只是在您設置新數據時通知觀察者。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.