IllegalStateException: is not currently in the FragmentManager

后端 未结 12 2131
予麋鹿
予麋鹿 2021-01-31 08:40

I know it sounds like a duplicate of FragmentStatePagerAdapter IllegalStateException: is not currently in the FragmentManager but his solution isn\'t relevan

12条回答
  •  迷失自我
    2021-01-31 09:32

    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!

提交回复
热议问题