【问题标题】:IllegalStateException: <MyFragment> is not currently in the FragmentManagerIllegalStateException: <MyFragment> 当前不在 FragmentManager 中
【发布时间】:2012-08-13 15:37:18
【问题描述】:

我知道这听起来像是 FragmentStatePagerAdapter IllegalStateException: <MyFragment> is not currently in the FragmentManager 的复制品 但他的解决方案与我的情况无关。

我很少遇到以下崩溃:

java.lang.RuntimeException:无法暂停活动 {MyActivity}:

...

原因:java.lang.IllegalStateException: Fragment MyFragment {40648258 id=0x7f070051} 目前不在 FragmentManager 中 android.support.v4.app.FragmentManagerImpl.putFragment(MT:516) 在 android.support.v4.app.FragmentStatePagerAdapter.saveState(MT:185) 在 android.support.v4.view.ViewPager.onSaveInstanceState(MT:881)

...

在 android.view.View.saveHierarchyState(View.java:6238) 在 com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1522) 在 android.app.Activity.onSaveInstanceState(Activity.java:1138) 在 android.support.v4.app.FragmentActivity.onSaveInstanceState(MT:480) 在 MyActivity.onSaveInstanceState(MT:336)

这似乎是我无法从FragmentStatePagerAdapter 理解的奇怪代码:

for (int i=0; i<mFragments.size(); i++) {
    Fragment f = mFragments.get(i);
    if (f != null) {
        if (state == null) {
            state = new Bundle();
        }
        String key = "f" + i;
        mFragmentManager.putFragment(state, key, f);
    }
}

看起来适配器从mFragments 获取了我的Fragment,但无法将其状态添加到FragmentManager

我找不到任何方法在我的测试设备上重新创建它,只从一些用户那里收到。

我正在使用支持包 v4。

有什么帮助吗? 谢谢。

【问题讨论】:

  • 检查thisthis
  • 我遇到了类似的错误,Alejandro Colorado 建议的链接解决了它。粗略查看支持库源后,片段状态看起来像是“未激活”(mIndex

标签: java android android-fragments android-viewpager illegalstateexception


【解决方案1】:

FragmentStatePagerAdapter 是一段可怕的代码,其中充斥着谷歌承认或不承认的错误,所以我使用这段代码来修复这个特定的崩溃:

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // Yet another bug in FragmentStatePagerAdapter that destroyItem is called on fragment that hasnt been added. Need to catch
        try {
            super.destroyItem(container, position, object);
        } catch (IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

【讨论】:

  • 我也面临这个问题
  • 也结束了这条快速路线,我们的 bug 也在 destroyItem 中
【解决方案2】:

当在 ViewPager 上设置 FragmentStatePagerAdapter 时,可能会发生这种情况,并且 Fragment 已填充,但由于在 ViewPager 发生布局之前 Activity 暂停而尚未添加到 FragmentManager。这可以通过从 onCreate 启动另一个 Activity 来可靠地实现,适配器也在 onCreate 中设置。在这种情况下,最好不要设置 Adapter,而是将其设置为 onActivityResult。看到这个问题:https://code.google.com/p/android/issues/detail?id=77285

我还提交了一个补丁来检查是否在尝试保存状态之前添加了 Fragment。

【讨论】:

  • 谢谢,这听起来很有希望。我去看看。
  • @marmor 您可以在 Adapter 实现中覆盖 saveState,如果 FragmentManager 没有 Fragment,则跳过调用 super,和/或将 super.saveState 包装在 try/catch 中以处理 IllegalStateException。
  • 这是解决方案吗,因为补丁仍然没有包含在内?
【解决方案3】:

如果您有片段层次结构,请使用 getChildFragmentManager() 而不是 getFragmentManager()。例如。如果你有碎片寻呼机。

【讨论】:

  • 谢谢,但我没有使用片段层次结构,一个活动下只有 3 个片段。
  • 我正在使用一个带有片段的活动,其中有一个浏览器管理片段。所以更改为 getChildFragmentManager() 为我解决了问题
  • 如何使用ActionBarActivity 完成此操作?我只有getSupportFragmentManager()
  • 我们在进行普通活动时无法访问 getChildFragmentManager。
【解决方案4】:

我遇到了确切的异常。

就我而言,我有几个 FragmentPagerStateAdapter 管理的片段,但有时片段会切换到位。

我重写 getItemPosition() 并返回新位置并调用 notifyDataSetChanged() 以刷新 ViewPager。一切正常,但有时当我离开 ViewPager 时会发生崩溃。

经过几个小时的研究,我发现 FragmentPagerStateAdapter 中存在一个错误。

适配器不仅缓存状态,还缓存它认为“活跃”的片段

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

但它不会检查片段的位置是否已失效或移动。

它就是这样得到这个异常的:

1.ViewPager和Adapter中有A,B,C三个Fragment。

2.我在适配器中将位置切换到B,A,C并调用notifyDataSetChanged()。

3.ViewPager 按新顺序重新布局片段。但是 mFragments 缓存仍然是 {A,B,C}

4.Swipe 几页,ViewPager 会要求adapter 销毁第一个Fragment。 然后片段 B,而不是 A,在缓存中设置为 null。 现在 mFragments 为 {null,A,C}。

5.离开ViewPager,触发onSaveInstanceState。 mFragments 中的所有片段都被认为是活动的,并且调用 putFragment() 直到 FragmentManagerImpl 发现 A 不在其中并抛出异常。

所以这是我不那么温和的解决方案:

1.复制整个FragmentPagerStateAdapter源代码。

2.重写 notifyDataSetChanged() 方法以重新排列缓存,如下所示。

@Override
public void notifyDataSetChanged() {
  List<Fragment> oldFragments = new ArrayList<>(mFragments);
  List<Fragment.SavedState> 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();
}

希望对你们中的一些人有用!

【讨论】:

  • 我建议在 Android 问题跟踪器上打开一个错误报告,因为您已经有修复它,您甚至可以直接将您的修复提交到 Android Gerrit 审查系统:android-review.googlesource.com
【解决方案5】:

如果您的 ViewPager 布局在片段内(不是活动):

mViewPager.setAdapter(new MyFragmentStatePagerAdapter(get<b>Child</b>FragmentManager()));

【讨论】:

    【解决方案6】:

    使用下面的

    final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(f, key);
    

    而不是

    mFragmentManager.putFragment(state, key, f);
    

    并明确传递捆绑包..

    供参考,

    http://developer.android.com/reference/android/app/FragmentTransaction.html#add%28android.app.Fragment,%20java.lang.String%29

    【讨论】:

    • 感谢您的回答,但有问题的代码来自 Android 的一个类 FragmentStatePagerAdapter,而不是来自我的包。重新阅读我的问题。
    • FragmentStatePagerAdapter 用于维护片段的状态。
    • 它用于显示多个片段并排显示,允许用户在它们之间滑动,同时保持状态。我正在使用这个类来展示一些片段,而且我很少遇到上述崩溃。
    • 像这种情况下,我在向 fragmentmanager 添加 mapfragment 时收到 IllegalArgumentException,即使我在删除了之前添加的 mapfragment 之后也是如此。它表明父母有这样一个具有相同 ID 的孩子。我已经实现了 try catch,因为我找不到其他方法来解决它。但我正在分析这个问题。让你回到确切的解决方案。
    【解决方案7】:

    您是在自己的代码中调用 FragmentStatePagerAdapter.instantiateItem() 吗?通常,ViewPager 应该是调用此方法的唯一客户端,但是,很容易遇到依赖此方法的其他限制的变通方法。

    由于FragmentStatePagerAdapter 的工作方式,我猜适配器已经实例化了 Fragment,将其添加到其内部的 Fragment 集合中,甚至开始了将 Fragment 添加到 FragmentManager 的事务,但是该 Fragment 事务具有未提交或执行。这正是 instantiateItem() 在尚未实例化请求的 Fragment 时所做的事情。

    另一种方法FragmentStatePagerAdapter.finishUpdate() 负责提交事务以及执行挂起的事务。 finishUpdate() 必须在 instantiateItem() 之后调用 否则 Fragment 可能无法添加到 FragmentManager。

    试试这样的:

    // Get the current Fragment presented by the ViewPager.
    pagerAdapter.startUpdate(viewPager);
    Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem());
    pagerAdapter.finishUpdate(viewPager);
    return fragment;
    

    从 API 级别 19 开始,startUpdate() 的实现为空。

    【讨论】:

      【解决方案8】:

      在Activity中写入onCreate()方法:

      pager = (ViewPager) findViewById(R.id.pager);
          adapter = new SwipePagerAdapter(getSupportFragmentManager());
          pageOneFragment = new PageOneFragment();
          adapter.addFragment(pageOneFragment);
      

      适配器代码:

      public class SwipePagerAdapter extends FragmentStatePagerAdapter
      {
      private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
      
      public SwipePagerAdapter(FragmentManager fm)
      {
          super(fm);
      }
      
      @Override
      public Fragment getItem(int position)
      {
          return mFragments.get(position);
      }
      
      @Override
      public int getCount()
      {
          return mFragments.size();
      }
      
      public void addFragment(Fragment fragment)
      {
          mFragments.add(fragment);
          notifyDataSetChanged();
      }
      
      @Override
      public void destroyItem(ViewGroup container, int position, Object object)
      {
          super.destroyItem(container, position, object);
      }
      
      @Override
      public CharSequence getPageTitle(int position)
      {
          return super.getPageTitle(position);
      }}
      

      【讨论】:

        【解决方案9】:

        你可以试试mFragmentManager.add();

        【讨论】:

        【解决方案10】:

        尝试使用

        use fragmentTransaction.add()
        

        【讨论】:

        • 请扩展您的答案,使其更好、更易于使用。
        【解决方案11】:

        ViewPager 中的 Fragments 是固定的,而不是尝试替换适配器中的 Fragment,尝试给不同的 Fragment 集并更改 notifyDataSet,或者利用 FrameLayout 在视图分页器选项卡的当前 Fragment 上显示另一个 Fragment .

        我的解决方案有效:

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

        【讨论】:

          【解决方案12】:

          仔细检查您是否在 Activity 中实现了 onSaveInstanceStateonRestoreInstanceState,并验证它们是否正确保存和加载片段的状态。

          【讨论】:

            猜你喜欢
            • 2012-07-03
            • 2014-03-01
            • 2013-07-12
            • 1970-01-01
            • 1970-01-01
            • 2016-12-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多