Scenario: I have two fragments named FirstFragment
and UnitFragment
. I go from FirstFragment
to UnitFragment
to select a unit to come back to FirstFragmet
using navController.popBackStack();
and send unit data to FirstFragment
which is observing unit data.
This is my 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()));
}
}
}
This is the LogCat result:
--- 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
As you see in LogCat result, every time I click the button and go to UnitFragment
and come back to FirstFragment
the onViewCreated
will call again and API LiveDataObserver will be triggered TWO times!!!
I know onViewCreated will call again because Navigation Component replaces the fragments instead of adding them. but I don't know why LiveData observer is being triggered two times.
I read this post but he seems to have ignored Navigation Component.
I need a solution to ...
onViewCreated
codes again.The observers method void onChanged(@Nullable T t) is called twice. That's fine. The first time it is called upon startup. The second time it is called as soon as Room has loaded the data.
Attach the Observer object to the LiveData object using the observe() method. The observe() method takes a LifecycleOwner object. This subscribes the Observer object to the LiveData object so that it is notified of changes. You usually attach the Observer object in a UI controller, such as an activity or fragment.
You should manually call removeObserver(Observer) to stop observing this LiveData. While LiveData has one of such observers, it will be considered as active.
Unfortunately, this is not the answer to your problems:
I need a solution to ...
Avoid calling onViewCreated codes again.
Avoid triggering LiveData observer again.
I'm trying to explain about navigation and its behaviors or correct some misunderstandings. These issues have different reasons and Avoid calling onViewCreated codes again.
is a devious way.
I know onViewCreated will call again because Navigation Component replaces the fragments instead of adding them.
As you know, replacing fragments when they were added in back-stack, just detaching old fragment from fragmentManager
. It means the old fragment's view will destroy.
And will create its view when you pop UnitFragment from the back-stack.
So don't call any API call in onViewCreated
because it may call multiple times (in configuration changes, destroying fragment, etc...)
It's better to use onCreate
for initializing non-view-related components (ViewModel + API calls). It reduces Lazy(!?) Initialization
check.
@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);
}
And start to observe them in onViewCreated
. Also, you should consume unit_data
from navBackStackEntry when you got it.
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());
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With