java.lang.IllegalStateException: Fragment no longer exists for key f1: index 3

AndroidAndroid Fragments

Android Problem Overview


I want to understand this exception in order to implement a proper fix.

There's a ViewPager and it uses a FragmentStatePagerAdapter to instantiate 2 fragments via getItem and MyFragmentClass.newInstance(...).

Adapter's getItem looks like this:

@Override
public Fragment getItem(int position) {
	Fragment fragment = null;
	
	switch(position) {
    	case 0:
    		fragment = MyFragment2.newInstance(par1);
    		break;
    	case 1:
    		fragment = MyFragment2.newInstance(par2, par3);
    		break;
	}
	return fragment;
}

Problem:

When the activity is destroyed, and created again, the adapter is intantiated again, the fragments created again with MyFragmentClass.newInstance(...)... but then on this line:

pager.setAdapter(adapter);

I get the mentioned exception.

I looked in the source where the exception is thrown, it's this:

@Override
public Fragment getFragment(Bundle bundle, String key) {
    int index = bundle.getInt(key, -1);
    if (index == -1) {
        return null;
    }
    if (index >= mActive.size()) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    Fragment f = mActive.get(index);
    if (f == null) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    return f;
}

So a bundle is passed there, with some state which references my old fragments, but this doesn't correspond to the current state (mActive), and the exception is thrown.

I don't understand what's the idea behind this, or which way I'm supposed to instantiate the fragments.

I tried a trick I got from another context:

pager.setOffscreenPageLimit(1);

In order to avoid that the fragments are destroyed when they are off screen (in the case of 2 pages viewpager, although don't know if it works well with state adapter). But don't seems to be related, at least, it doesn't help, still get the same exception.

Catching the exception leads to the pages being blank.

Android Solutions


Solution 1 - Android

This could help -

@Override
public Parcelable saveState() {
    return null;
}

Add the line above in FragmentStatePagerAdapter.

Solution 2 - Android

If you don't want the fragments to get reclaimed when they are offscreen, you should be using FragmentPagerAdapter and not FragmentStatePagerAdapter.

Solution 3 - Android

Issue Detail

By default FragmentStatePagerAdapter will save and restore the state of ViewPager. While restore if fragment instance killed due to some reason then FragmentManger will throw this exception.

Solution:

To Fix this need to override the restoreState method in our FragmentStatePagerAdapter and put try catch block. It will prevent the crash and also it will retain the viewpager's fragment state for normal case.

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
    try {
        super.restoreState(state, loader);
    } catch (Exception e) {
        Log.e("TAG", "Error Restore State of Fragment : " + e.getMessage(), e);
    }
}

Note: We can use FragmentPagerAdapter or Override saveState() and return null also fix this issue but viewpager will not retain its state for the normal case.

Solution 4 - Android

if you are using ViewPager2 then use this method on ViewPager2 object

viewPager2.setSaveEnabled(false);

Solution 5 - Android

I have struggle on this problem for whole day, but now I found the solution.

private ViewPager _mViewPager;
_mViewPager.setOffscreenPageLimit(5);
//5 is how much page you have.

setOffscreenPageLimit Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.

Solution 6 - Android

Use Activity lifeCycle instead of Fragment.

public class MyAdapter extends FragmentStateAdapter {

    public MyAdapter (@NonNull Fragment fragment) {
        super(fragment.getFragmentManager(), fragment.getActivity().getLifecycle());
    }
}

Or as others mentioned disable the ViewPager2 save state.

  • in Layout: android:saveEnabled="false"
  • in code:
    • viewPager.setSaveEnabled(false);
    • viewPager.setSaveFromParentEnabled(false);

Solution 7 - Android

very good ! Since is repeat pager.setAdapter(adapter); when call restoreState cause exception:

Fragment no longer exists for key f0: index 0

we can Release Fragment RootView

    public void onDestroyView() {
    super.onDestroyView();
    if (isRecyclerRootViewAlways()) {
        mRootView = null;//<--
    }
    mMyFragmentLifecycle.onFragmentDestroyView(this);
}

Solution 8 - Android

Use

> getFragmentManager()

in fragment adapter not child fragment manager

PagerAdapter adapter = new PagerAdapter(getFragmentManager());

I hope this helps.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUserView Question on Stackoverflow
Solution 1 - AndroidMikelis KanepsView Answer on Stackoverflow
Solution 2 - AndroidtadView Answer on Stackoverflow
Solution 3 - AndroidVickyView Answer on Stackoverflow
Solution 4 - AndroidnabeelView Answer on Stackoverflow
Solution 5 - AndroidYi WangView Answer on Stackoverflow
Solution 6 - AndroidIlya GazmanView Answer on Stackoverflow
Solution 7 - Android任非凡View Answer on Stackoverflow
Solution 8 - AndroidAmit KumarView Answer on Stackoverflow