FragmentStatePagerAdapter IllegalStateException:

2019-01-22 03:02发布

问题:

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;
}

I also can't debug through the source since the support package doesn't let me attach the source, for some reason... but that's a different question...

回答1:

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

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



回答2:

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.



回答3:

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?



回答4:

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.



回答5:

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.



回答6:

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.



回答7:

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