I have an app which is having Master/Detail kind of architecture. When I select some item, the details are displayed in details fragment.
For the detail fragment to know what to load id
is sent over via Arguments
. Using SavedStateHandle
in VM I can directly read these arguments from the handle without re-routing it in Fragment
itself. That works great for the first detail fragment. The problem comes with next selections.
Every time I load details, the id
from first selection is populated although every time a new fragment is created along with new ViewModel.
I was looking into the code of lifecycle-viewmodel-savedstate library (v.2.3.1) and found this method in SavedStateHandle
called:
static SavedStateHandle createHandle(@Nullable Bundle restoredState,
@Nullable Bundle defaultState) {
if (restoredState == null && defaultState == null) {
return new SavedStateHandle();
}
Map<String, Object> state = new HashMap<>();
if (defaultState != null) {
for (String key : defaultState.keySet()) {
state.put(key, defaultState.get(key));
}
}
if (restoredState == null) {
return new SavedStateHandle(state);
}
ArrayList keys = restoredState.getParcelableArrayList(KEYS);
ArrayList values = restoredState.getParcelableArrayList(VALUES);
if (keys == null || values == null || keys.size() != values.size()) {
throw new IllegalStateException("Invalid bundle passed as restored state");
}
for (int i = 0; i < keys.size(); i++) {
state.put((String) keys.get(i), values.get(i));
}
return new SavedStateHandle(state);
}
Here I can see that in defaultState
the correct id is set for every new view model. But as you can see defaultState
is processed first and restoredState
is processed after that. restoredState
contains same key with old id
which at the end replace the correct one from defaultState
.
I can understand that is probably wanted behavior for real restoring but in my case I'm not restoring the fragment. Yes, the class is same but I'm just replacing detail fragment with another detail fragment with new/different data.
Am I doing something wrong? Can I give the framework a hint that this is not restoration and I'm not interested in saved values from old fragment?
What I didn't mentioned was that I'm using
ViewPager2
for Master/Detail view. I didn't consider it having some influence on state staving but it has.ViewPager2
library is saving and restoring state on the background which is then used bySavedStateHandle
. To map fragments with states it uses ids fromFragmentStateAdapter
. I didn't overridegetItemId
method so ids were assigned by position.This of course map same state to new fragments on the exact position. Implementing
getItemId
which assign different ids to different instances of fragment fixed the problem.