I know it sounds like a duplicate of FragmentStatePagerAdapter IllegalStateException:
I've encountered the exact exception.
In my case, I have several fragments managed by FragmentPagerStateAdapter, but sometimes the fragment will get switched in position.
I override getItemPosition() and return the new position and call notifyDataSetChanged() to refresh ViewPager. Everything works fine but sometimes when I leave the ViewPager the crash occurs.
After several hours digging into it, I found there's a bug in FragmentPagerStateAdapter.
The adapter not only caches states, but also caches the fragments that it consider 'active'
private ArrayList mSavedState = new ArrayList();
private ArrayList mFragments = new ArrayList();
But it does not check if the fragments' position has invalidated or moved.
That's how it get this exception:
1.I have A,B,C three Fragments in ViewPager and Adapter.
2.I switched the position to B,A,C in adapter and called notifyDataSetChanged().
3.ViewPager re-layout the fragment by new order. But the mFragments cache is still {A,B,C}
4.Swipe few pages, ViewPager will ask adapter to destroy the first Fragment. then the fragment B, instead of A, is set to null in the cache. Now the mFragments is {null,A,C}.
5.Leave the ViewPager, and onSaveInstanceState is triggered. all fragments in mFragments is considered active and putFragment() is called until FragmentManagerImpl found A not in it and throw a Exception.
So here's my not so gentle solution:
1.Copy the entire FragmentPagerStateAdapter source code.
2.Override the notifyDataSetChanged() method to rearrange the caches as below.
@Override
public void notifyDataSetChanged() {
List oldFragments = new ArrayList<>(mFragments);
List oldStates = new ArrayList<>(mSavedState);
for (int i = 0; i < getCount(); i++) {
if (i < mFragments.size()) {
Fragment f = mFragments.get(i);
if (f != null) {
int newPosition = getItemPosition(f);
if (newPosition == POSITION_UNCHANGED || newPosition == i) {
} else if (newPosition == POSITION_NONE) {
if (i < mSavedState.size()) {
mSavedState.set(i, null);
}
mFragments.set(i, null);
} else {
while (i >= mFragments.size()) {
mFragments.add(null);
}
if (oldStates.size() > i) {
mSavedState.set(newPosition, oldStates.get(i));
}
mFragments.set(newPosition, oldFragments.get(i));
}
} else {
/*
* No Fragment but that's possible there's savedState and position has
* changed.
*/
}
}
}
super.notifyDataSetChanged();
}
Hope that works for some of you!