1. Bug的意思
2. Bug出现的场景
当ViewPager包裹Fragment,并且Fragment里面又有ViewPager包裹Fragment时。 当外层的Fragment或者里层的的Adapter继承FragmentStatePagerAdapter,或两者都继承FragmentStatePagerAdapter时。
3. 解决的方法(根据具体情况做选择)-------(我)实用的方法一
在报错的Fragment的Adapter中重写以下方法,返回空
@Override
public Parcelable saveState () {
return null ;
}
不要使用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
public void setAdapter(PagerAdapter adapter) { if (mAdapter != ) { mAdapter.unregisterDataSetObserver(mObserver); mAdapter.startUpdate(this ); for ( int i = 0 ; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); mAdapter.destroyItem(this , ii.position, ii.object); } mAdapter.finishUpdate(this ); mItems.clear(); removeNonDecorViews(); mCurItem = 0 ; scrollTo(0 , 0 ); } final PagerAdapter oldAdapter = mAdapter; mAdapter = adapter; mExpectedAdapterCount = 0 ; if (mAdapter != ) { if (mObserver == ) { mObserver = new PagerObserver(); } mAdapter.registerDataSetObserver(mObserver); mPopulatePending = false ; final boolean wasFirstLayout = mFirstLayout; mFirstLayout = true ; mExpectedAdapterCount = mAdapter.getCount(); if (mRestoredCurItem >= 0 ) { mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); setCurrentItemInternal(mRestoredCurItem, false , true ); mRestoredCurItem = -1 ; mRestoredAdapterState = ; mRestoredClassLoader = ; } else if (!wasFirstLayout) { populate(); } else { requestLayout(); } } if (mAdapterChangeListener != && oldAdapter != adapter) { mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter); } }
2. FragmentStatePagerAdapter.restoreState(Parcelable state, ClassLoader loader) {
Fragment f = mFragmentManager.getFragment(bundle, key);
3. android.support.v4.app.FragmentManager.getFragment
@Override public Fragment getFragment(Bundle bundle, String key) { int index = bundle.getInt(key, - 1 ); if (index == - 1 ) { return ; } if (index >= mActive.size()) { throwException(new IllegalStateException( "Fragement no longer exists for key " + key + ": index " + index)); } Fragment f = mActive.get(index); if (f == ) { throwException(new IllegalStateException( "Fragement no longer exists for key " + key + ": index " + index)); } return f; }
从bundle中可以取出来,但是有两种情况会抛出此异常,吐槽下为啥两种情况抛出的日志一模一样。
1.没有存活的Fragment
2.获取到的Fragemnt为空
* 问题是Bundle从哪里来的?
从以上ViewPager.setAdapter代码中可以看出传递的bundle是ViewPager.mRestoredAdapterState,在ViewPager的上下文进行查找此变量,可获知仅以下一个函数onRestoreInstanceState中进行了赋值操作。
@Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super .onRestoreInstanceState(state); return ; } SavedState ss = (SavedState)state; super .onRestoreInstanceState(ss.getSuperState()); if (mAdapter != ) { mAdapter.restoreState(ss.adapterState, ss.loader); setCurrentItemInternal(ss.position, false , true ); } else { mRestoredCurItem = ss.position; mRestoredAdapterState = ss.adapterState; mRestoredClassLoader = ss.loader; } }
通过函数名可以看出跟状态的存储与取出有关,详情可以查看以下文章。
《View的onSaveInstanceState和onRestoreInstanceState过程分析》
由onRestoreInstanceState源码可以看出,出问题处的bundle是从此函数的state参数中获取的。既然onRestoreInstanceState这个函数是获取存储的状态,就来看下保存状态onSaveInstanceState的代码。
@Override public Parcelable onSaveInstanceState() { Parcelable superState = super .onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.position = mCurItem; if (mAdapter != ) { ss.adapterState = mAdapter.saveState(); } return ss; }
由以上可以看出只有在此ViewPager已经设置过mAdapter才会触发saveState(),接着来看下FragmentStatePagerAdapter.saveState()的源码
@Override public Parcelable saveState() { Bundle state = ; if (mSavedState.size() > 0 ) { state = new Bundle(); Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; mSavedState.toArray(fss); state.putParcelableArray("states" , fss); } for ( int i= 0 ; i<mFragments.size(); i++) { Fragment f = mFragments.get(i); if (f != ) { if (state == ) { state = new Bundle(); } String key = "f" + i; mFragmentManager.putFragment(state, key, f); } } return state; }
由此找到了Bundle的根源,网上解决此BUG的一种解法就是覆写此函数返回为空,这样FragmentManager.getFragment函数中就不满足第一个判断条件,不会执行后续代码也不会抛出异常了。
@Override public Parcelable saveState() { return ; }
文章地址:
《java.lang.IllegalStateException: Fragement no longer exists for key f1: index 3》