【问题标题】:java.lang.IllegalStateException: Fragment no longer exists for key f1: index 3java.lang.IllegalStateException:键 f1 的片段不再存在:索引 3
【发布时间】:2012-07-30 03:55:28
【问题描述】:

我想了解这个异常以便实施适当的修复。

有一个 ViewPager,它使用 FragmentStatePagerAdapter 通过 getItem 和 MyFragmentClass.newInstance(...) 实例化 2 个片段。

Adapter 的 getItem 如下所示:

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

问题:

当 Activity 被销毁并再次创建时,适配器再次实例化,片段再次使用MyFragmentClass.newInstance(...)... 创建但随后在这一行:

pager.setAdapter(adapter);

我得到了提到的异常。

我查看了引发异常的源,是这样的:

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

因此在那里传递了一个包,其中一些状态引用了我的旧片段,但这与当前状态不对应(mActive),并引发了异常。

我不明白这背后的想法是什么,或者我应该以哪种方式实例化片段。

我尝试了一个从另一个上下文中得到的技巧:

pager.setOffscreenPageLimit(1);

为了避免片段在屏幕外时被破坏(在2页viewpager的情况下,虽然不知道它是否适用于状态适配器)。但是好像没有关系,至少,没用,还是报同样的异常。

捕获异常会导致页面空白。

【问题讨论】:

  • 您找到解决问题的有效方法了吗?
  • 你找到解决办法了吗
  • 我不记得了,抱歉:(
  • 这个问题是 2 年前提出的。如今,使用了 AppcompatActivity 和支持片段。使用 setRetianInstance(true);并记住使用方向上的现有项目重新创建适配器。如果您不重新创建适配器,它将尝试使用垃圾上下文,这将导致崩溃。
  • 线程code.google.com/p/android/issues/detail?id=54520中讨论了同样的问题

标签: android android-fragments


【解决方案1】:

这可能会有所帮助 -

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

FragmentStatePagerAdapter中添加上面的行。

【讨论】:

  • 这非常适合处理后台堆栈中的分页适配器。谢谢!
  • 如果您避免使用saveState(),则不再需要使用FragmentStatePagerAdapter。然后你只需要使用FragmentPagerAdapter 来工作
  • @Nishad 什么不是真的?
  • FragmentStatePagerAdapter 避免saveState() 不等同于使用FragmentPagerAdapterFragmentStatePagerAdapter 在不使用时销毁片段对象 FragmentPagerAdapter 不会。通过避免 saveState() 我们要求FragmentStatePagerAdapter 在不保存状态的情况下销毁片段。
  • @Lennon FragmentPagerAdapter 不使用 saveState() 因为整个片段都保留在内存中。如果你想避免这种行为,你可以使用FragmentStatePagerAdapter,它只保存状态。如果您根本不想保存任何内容,那么您使用FragmentStatepagerAdapter 而不保存状态;它不会自动将Fragment 保存在内存中。
【解决方案2】:

如果您不希望碎片在屏幕外时被回收,您应该使用FragmentPagerAdapter 而不是FragmentStatePagerAdapter

【讨论】:

  • 这种情况有什么不同?
  • FragmentPagerAdapter 在访问时将每个片段加载到内存中。一旦片段超过offscreenPageLimit 参数,FragmentStatePagerAdapter 就会释放其对片段的引用。
  • 所以如果我们设置一个 offscreenpagelimit 就不再是问题了吗? (对于FragmentStatePagerAdapter
  • 不是,原发帖人做错了其他事情,比如项目数设置错误。
【解决方案3】:

问题详情

默认情况下,FragmentStatePagerAdapter 会保存和恢复 ViewPager 的状态。在恢复时,如果片段实例由于某种原因被杀死,那么 FragmentManger 将抛出此异常。

解决方案:

要解决此问题,需要覆盖我们的 FragmentStatePagerAdapter 中的 restoreState 方法并放置 try catch 块。它将防止崩溃,并且它会在正常情况下保留 viewpager 的片段状态。

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

注意:我们可以使用 FragmentPagerAdapter 或 Override saveState() 并返回 null 也可以解决此问题,但 viewpager 在正常情况下不会保留其状态。

【讨论】:

  • 谢谢@Vicky,很有魅力,这应该被标记为正确答案
  • 不能覆盖,它被设置为final :(
【解决方案4】:

如果您正在使用 ViewPager2,请在 ViewPager2 对象上使用此方法

viewPager2.setSaveEnabled(false);

【讨论】:

    【解决方案5】:

    我在这个问题上挣扎了一整天,但现在我找到了解决方案。

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

    setOffscreenPageLimit 设置在空闲状态下视图层次结构中当前页面任一侧应保留的页面数。超出此限制的页面将在需要时从适配器重新创建。

    【讨论】:

    • 这会占用大量内存...省去了不保存状态和所有内容的麻烦...但是从长远来看,当有大量数据时,这很糟糕
    【解决方案6】:

    使用 Activity 生命周期而不是 Fragment。

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

    或者像其他人提到的那样禁用ViewPager2 保存状态。

    • 在布局中:android:saveEnabled="false"
    • 在代码中:
      • viewPager.setSaveEnabled(false);
      • viewPager.setSaveFromParentEnabled(false);

    【讨论】:

      【解决方案7】:

      非常好!由于在调用restoreState 时重复pager.setAdapter(adapter); 导致异常:

      Fragment no longer exists for key f0: index 0
      

      我们可以释放 Fragment RootView

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

      【讨论】:

        【解决方案8】:

        使用

        getFragmentManager()

        在片段适配器中 不是子片段管理器

        PagerAdapter adapter = new PagerAdapter(getFragmentManager());
        

        我希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 2018-01-25
          • 2020-07-22
          • 2020-04-16
          • 1970-01-01
          • 1970-01-01
          • 2018-03-25
          • 2018-05-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多