先来看下异常信息
java.lang.IllegalStateException: Fragement no longer exists for key f0: index 0at 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
- /**
- * Set a PagerAdapter that will supply views for this pager as needed.
- *
- * @param adapter Adapter to use
- */
- public void setAdapter(PagerAdapter adapter) {
- if (mAdapter != null) {
- 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 != null) {
- if (mObserver == null) {
- 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 = null;
- mRestoredClassLoader = null;
- } else if (!wasFirstLayout) {
- populate();
- } else {
- requestLayout();
- }
- }
- if (mAdapterChangeListener != null && 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 null;
- }
- if (index >= mActive.size()) {
- throwException(new IllegalStateException("Fragement no longer exists for key "
- + key + ": index " + index));
- }
- Fragment f = mActive.get(index);
- if (f == null) {
- 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 != null) {
- 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 != null) {
- ss.adapterState = mAdapter.saveState();
- }
- return ss;
- }
由以上可以看出只有在此ViewPager已经设置过mAdapter才会触发saveState(),接着来看下FragmentStatePagerAdapter.saveState()的源码
- @Override
- public Parcelable saveState() {
- Bundle state = null;
- 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 != null) {
- if (state == null) {
- state = new Bundle();
- }
- String key = "f" + i;
- mFragmentManager.putFragment(state, key, f);
- }
- }
- return state;
- }
由此找到了Bundle的根源,网上解决此BUG的一种解法就是覆写此函数返回为空,这样FragmentManager.getFragment函数中就不满足第一个判断条件,不会执行后续代码也不会抛出异常了。
- @Override
- public Parcelable saveState() {
- return null;
- }
文章地址:
《java.lang.IllegalStateException: Fragement no longer exists for key f1: index 3》