28

I'm getting this on some cases, in onResume(), of an activity which uses a FragmentStatePagerAdapter. When using device's back button. Not always. Not reproducible.

I'm using support package v4, last revision (8).

Already searched with google, no success finding a useful answer.

Looking in the source, it's thrown here: FragmentManager.java

@Override
public void putFragment(Bundle bundle, String key, Fragment fragment) {
    if (fragment.mIndex < 0) {
        throw new IllegalStateException("Fragment " + fragment
                + " is not currently in the FragmentManager");
    }
    bundle.putInt(key, fragment.mIndex);
}

But why is the index of fragment < 0 there?

The code instantiating the fragments:

@Override
public Fragment getItem(int position) {
    Fragment fragment = null;

    switch(position) {
        case 0:
            fragment = MyFragment.newInstance(param1);
            break;
        case 1:
            fragment = MyFragment2.newInstance(param2, param3);
            break;
    }
    return fragment;
}

@Override
public int getCount() {
    return 2;
}
User
  • 31,811
  • 40
  • 131
  • 232
  • Are you calling FragmentStatePagerAdapter.instantiateItem() from your code? http://stackoverflow.com/a/20210413/204480 – James Wald Nov 26 '13 at 07:07

7 Answers7

15

If your ViewPager is layouted inside a fragment (not an activty) :

mViewPager.setAdapter(new MyFragmentStatePagerAdapter(getChildFragmentManager()));

Thomas G.
  • 882
  • 9
  • 10
  • 2
    Thank you for this. This "getChildFragmentManager()" does work fine! – 1111161171159459134 Feb 04 '15 at 20:42
  • 3
    Didn't worked for me. I have a nested viewpager and i use the getChildFragmentManager() method in the parent fragment. The exception is thrown with that method. – Ricardo Ribas May 29 '15 at 23:59
  • 1
    You also saved my day!!! When leaving the fragment that has the viewpager, then coming back to it, the contents of the viewpager were empty. I tried many inefficient methods, like moving the pages back and forth programmatically to refresh the view, but the moment I used the getChildFragmentManager(), the contents came back to life! Can't thank you enough – usernotnull Dec 06 '15 at 19:10
10

I had the same error here, but for another reason.

In my case I had override getItemPosition in my FragmentStatePagerAdapter. My ideia was to return the position, if the item exists, or a POSITION_NONE, if it doesn't exists anymore.

Well, my problem was the fact that when my collection got empty I returned POSITION_NONE. And that broke everything.

My fix was to return POSITION_UNCHANGED when I had an empty collection.

Hope it helps someone else.

laggedHero
  • 126
  • 1
  • 4
  • thanks, I actually had the exact same scenario and it fixed it, BTW do you have a dynamic number of fragments or is it constant? – 242Eld Jul 31 '14 at 11:04
  • Hi @242Eld. It was a dynamic number of fragments. It depends on the data from a WS. – laggedHero Aug 04 '14 at 12:15
8

The two key things to understand the bug are:

  1. It happens sometimes.
  2. It happens in onResume().

Given this information, it's likely that the ViewPager is not retaining the state of your Fragments. If you are manipulating the Fragments directly from the Activity, it could be the case that the off-page Fragment is getting destroyed and your Activity is trying to manipulate a null fragment. To retain the Fragment's state even when it is not in the current screen, the fix is pretty simple:

private static final int NUM_ITEMS = 2;

ViewPager mPager = /** instantiate viewpager **/;
mPager.setOffscreenPageLimit(NUM_ITEMS-1);

You can read about it here:

ViewPager Fragments getting destroyed over time?

Community
  • 1
  • 1
Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • 2
    I don't understand the logic of throwing an exception because the ViewPager is not retaining the state of the fragments... If the fragment is destroyed, why the ViewPager don't just recreates it? And anyways, each time I load the activity I'm setting a new adapter which instantiates the fragments, I don't understand why it's still trying to access other fragments then........... – User Jul 19 '12 at 18:43
  • 28
    All this fragment stuff is really horrible, I already got 4 different exceptions, all related with trying to access something not existing anymore "fragment no longer exists", "no view found for id 0xcf for fragment", "fragment is no longer in the fragment manager" (and forgot the 4th one). The stacktraces don't even show any line of my code, and there are no usuful guidelines or examples about this, just some very simple tutorials, which always work. But as soon as it turn a bit more complicated... boom. – User Jul 19 '12 at 18:52
  • Hmm... yes, I remember being confused by the `ViewPager` lifecycle/management of fragments as well. It's not very well documented on the developers site, and the source code is rather dense -_- – Alex Lockwood Jul 19 '12 at 18:55
  • But does it makes sense after having studied the source? For me it looks like there were something wrong with the design / API... But if you say it's just lack of documentation, I will take time... someday... to study the source. – User Jul 19 '12 at 19:11
  • 2
    I'm not 100% sure that studying the source will help... I never felt like I had achieved a 100% working solution and was just waiting for my app to randomly break on me because the fixes I made felt unreliable... but maybe I did implement it correctly, who knows. All I know is that the documentation isn't very clear about how the `ViewPager` manages its container fragments so maybe studying the documentation can give you more insight on that... – Alex Lockwood Jul 19 '12 at 19:40
5

Got it, the reason was, that I'm intantiating the Adapter each time in onResume().

If I instantiate the adapter only once, in the life cycle of the activity, this does not happen anymore.

User
  • 31,811
  • 40
  • 131
  • 232
  • To be honest, I don't remember. If the exception happens very seldomly a try catch can help. Maybe the issue has been fixed in more recent versions of the support package (for example they allow now nested fragments, which caused exceptions in older versions). – User Jul 09 '13 at 20:19
4

This exceptions means that you are trying to attach a fragment to an activity which is no longer correct state to attach the fragments. What it means is, whenever we try to attach fragments (especially through an asynchronous call), there is a small probability that someone has pressed the back button and activity is in merge of getting destroyed while you are attaching the fragment. This is not always reproducible as its just a race condition, and might not always occur..

There are two ways to fix this:

1) This happens when you the onSaveInstanceState of your activity has been called and post to that you are trying to attach the fragment, since android wont be able to save the state of your fragment now, it will throw an exception. To overcome this and if you are not saving the state of your fragment, try using

commitAllowingStateLoss(), while committing the transaction.

2) To be very safe, check whether your activity is in correct state or not before attaching the fragment, use the following code in onPause:

    boolean isInCorrectState;

    public void onCreate{
    super.onCreate();
    isInCorrectState = true;
    }

    public void onPause() {
    super.onPause();
      if(isFinishing()){
        isInCorrectState = false;
       }
    }

Now use this flag to check if your activity is in correct state or not before attaching the fragment.. Meaning attach the fragment iff isInCorrectState == true.

Vivek Soneja
  • 850
  • 6
  • 7
2

For me the reason was something else.

In my Loader.onLoaderReset() I cleared the data from the adapter. When I was leaving the app, onDestroy() caused the loader to reset, which cleared the FragmentStatePagerAdapter. I think it caused the adapter to clear all references to it's Fragments, but somehow, the FragmentManager didn't notice and threw the Exception. Doesn't seem very logical to me.

Note that for me it happened Activity.onDestroy().

Hope it helps someone.

Michał Klimczak
  • 12,674
  • 8
  • 66
  • 99
  • I'm curious,how did you fix it? I'm encountering the same thing. When I removed the code to clear the adapter called during onLoaderReset, I am not getting the error anymore. Just worried about leaks.. – alpinescrambler Jan 30 '14 at 05:49
  • I wouldn't worry about leaks. Personally, I leave empty `onLoaderReset()` most of the time. See this answer, it provides more detail: http://stackoverflow.com/a/16337401/1118475 – Michał Klimczak Jan 30 '14 at 07:56
0

Fragments in the ViewPager are fixed, instead of trying to replace the fragments in the adapter, try to give a different set of fragments and notifyDataSet changed, or take the advantage of FrameLayout to show another fragment over the view pager tab's current fragment.

There is my solution that works:

Swipe Gesture applied at Fragment level along with ViewPager with it's default swipe disabled

Community
  • 1
  • 1
Abhinav Saxena
  • 1,990
  • 2
  • 24
  • 55