Fragment no longer exists for key f0: index 0

1. Bug的意思

  • Fragment为空
  • Fragment已被销毁

2. Bug出现的场景

  • 当ViewPager包裹Fragment,并且Fragment里面又有ViewPager包裹Fragment时。
  • 当外层的Fragment或者里层的的Adapter继承FragmentStatePagerAdapter,或两者都继承FragmentStatePagerAdapter时。

3. 解决的方法(根据具体情况做选择)-------(我)实用的方法一

  • 在报错的Fragment的Adapter中重写以下方法,返回空
@Override  
public Parcelable saveState() {  
    return null;  
} 
 
 
  • 1
  • 2
  • 3
  • 4
  • 不要使用FragmentStatePagerAdapter,而是让里外层Fragment的Adaper继承FragmentPagerAdapter。












先来看下异常信息

java.lang.IllegalStateException: Fragement no longer exists for key f0: index 0
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:564)
at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:211)
at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:427)



加载完数据后,调用setAdapter出错,根本原因是getFragment中出现的异常,接下来看下异常堆栈的源码,以下是ViewPager.setAdapter源码。
1. ViewPager.setAdapter
[java]  view plain  copy
  1. /** 
  2.   * Set a PagerAdapter that will supply views for this pager as needed. 
  3.   * 
  4.   * @param adapter Adapter to use 
  5.   */  
  6.  public void setAdapter(PagerAdapter adapter) {  
  7.      if (mAdapter != ) {  
  8.          mAdapter.unregisterDataSetObserver(mObserver);  
  9.          mAdapter.startUpdate(this);  
  10.          for (int i = 0; i < mItems.size(); i++) {  
  11.              final ItemInfo ii = mItems.get(i);  
  12.              mAdapter.destroyItem(this, ii.position, ii.object);  
  13.          }  
  14.          mAdapter.finishUpdate(this);  
  15.          mItems.clear();  
  16.          removeNonDecorViews();  
  17.          mCurItem = 0;  
  18.          scrollTo(00);  
  19.      }  
  20.   
  21.   
  22.      final PagerAdapter oldAdapter = mAdapter;  
  23.      mAdapter = adapter;  
  24.      mExpectedAdapterCount = 0;  
  25.   
  26.   
  27.      if (mAdapter != ) {  
  28.          if (mObserver == ) {  
  29.              mObserver = new PagerObserver();  
  30.          }  
  31.          mAdapter.registerDataSetObserver(mObserver);  
  32.          mPopulatePending = false;  
  33.          final boolean wasFirstLayout = mFirstLayout;  
  34.          mFirstLayout = true;  
  35.          mExpectedAdapterCount = mAdapter.getCount();  
  36.          if (mRestoredCurItem >= 0) {  
  37.              mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);  
  38.              setCurrentItemInternal(mRestoredCurItem, falsetrue);  
  39.              mRestoredCurItem = -1;  
  40.              mRestoredAdapterState = ;  
  41.              mRestoredClassLoader = ;  
  42.          } else if (!wasFirstLayout) {  
  43.              populate();  
  44.          } else {  
  45.              requestLayout();  
  46.          }  
  47.      }  
  48.   
  49.   
  50.      if (mAdapterChangeListener !=  && oldAdapter != adapter) {  
  51.          mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);  
  52.      }  
  53.  }  




2. FragmentStatePagerAdapter.restoreState(Parcelable state, ClassLoader loader) {
Fragment f = mFragmentManager.getFragment(bundle, key);


3. android.support.v4.app.FragmentManager.getFragment
[java]  view plain  copy
  1. @Override  
  2. public Fragment getFragment(Bundle bundle, String key) {  
  3.     int index = bundle.getInt(key, -1);  
  4.     if (index == -1) {  
  5.         return ;  
  6.     }  
  7.     if (index >= mActive.size()) {  
  8.         throwException(new IllegalStateException("Fragement no longer exists for key "  
  9.                 + key + ": index " + index));  
  10.     }  
  11.     Fragment f = mActive.get(index);  
  12.     if (f == ) {  
  13.         throwException(new IllegalStateException("Fragement no longer exists for key "  
  14.                 + key + ": index " + index));  
  15.     }  
  16.     return f;  
  17. }  


从bundle中可以取出来,但是有两种情况会抛出此异常,吐槽下为啥两种情况抛出的日志一模一样。
1.没有存活的Fragment
2.获取到的Fragemnt为空



* 问题是Bundle从哪里来的?
从以上ViewPager.setAdapter代码中可以看出传递的bundle是ViewPager.mRestoredAdapterState,在ViewPager的上下文进行查找此变量,可获知仅以下一个函数onRestoreInstanceState中进行了赋值操作。

[java]  view plain  copy
  1. @Override  
  2. public void onRestoreInstanceState(Parcelable state) {  
  3.     if (!(state instanceof SavedState)) {  
  4.         super.onRestoreInstanceState(state);  
  5.         return;  
  6.     }  
  7.   
  8.   
  9.     SavedState ss = (SavedState)state;  
  10.     super.onRestoreInstanceState(ss.getSuperState());  
  11.   
  12.   
  13.     if (mAdapter != ) {  
  14.         mAdapter.restoreState(ss.adapterState, ss.loader);  
  15.         setCurrentItemInternal(ss.position, falsetrue);  
  16.     } else {  
  17.         mRestoredCurItem = ss.position;  
  18.         mRestoredAdapterState = ss.adapterState;  
  19.         mRestoredClassLoader = ss.loader;  
  20.     }  
  21. }  



通过函数名可以看出跟状态的存储与取出有关,详情可以查看以下文章。
《View的onSaveInstanceState和onRestoreInstanceState过程分析》


由onRestoreInstanceState源码可以看出,出问题处的bundle是从此函数的state参数中获取的。既然onRestoreInstanceState这个函数是获取存储的状态,就来看下保存状态onSaveInstanceState的代码。
[java]  view plain  copy
  1. @Override  
  2. public Parcelable onSaveInstanceState() {  
  3.     Parcelable superState = super.onSaveInstanceState();  
  4.     SavedState ss = new SavedState(superState);  
  5.     ss.position = mCurItem;  
  6.     if (mAdapter != ) {  
  7.         ss.adapterState = mAdapter.saveState();  
  8.     }  
  9.     return ss;  
  10. }  


由以上可以看出只有在此ViewPager已经设置过mAdapter才会触发saveState(),接着来看下FragmentStatePagerAdapter.saveState()的源码
[java]  view plain  copy
  1. @Override  
  2. public Parcelable saveState() {  
  3.     Bundle state = ;  
  4.     if (mSavedState.size() > 0) {  
  5.         state = new Bundle();  
  6.         Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];  
  7.         mSavedState.toArray(fss);  
  8.         state.putParcelableArray("states", fss);  
  9.     }  
  10.     for (int i=0; i<mFragments.size(); i++) {  
  11.         Fragment f = mFragments.get(i);  
  12.         if (f != ) {  
  13.             if (state == ) {  
  14.                 state = new Bundle();  
  15.             }  
  16.             String key = "f" + i;  
  17.             mFragmentManager.putFragment(state, key, f);  
  18.         }  
  19.     }  
  20.     return state;  
  21. }  


由此找到了Bundle的根源,网上解决此BUG的一种解法就是覆写此函数返回为空,这样FragmentManager.getFragment函数中就不满足第一个判断条件,不会执行后续代码也不会抛出异常了。
[java]  view plain  copy
  1. @Override  
  2. public Parcelable saveState() {  
  3.     return ;  
  4. }  


文章地址:
《java.lang.IllegalStateException: Fragement no longer exists for key f1: index 3》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值