您的位置:首页 > 其它

Fragment no longer exists for key f0: index 0

2017-11-24 15:11 4996 查看


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

/** 

  * Set a PagerAdapter that will supply views for this pager as needed. 

  * 

  * @param adapter Adapter to use 

  */  

 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

[java] view
plain copy

@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中进行了赋值操作。

[java] view
plain copy

@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的代码。

[java] view
plain copy

@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()的源码

[java] view
plain copy

@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函数中就不满足第一个判断条件,不会执行后续代码也不会抛出异常了。

[java] view
plain copy

@Override  

public Parcelable saveState() {  

    return ;  

}  

文章地址:
《java.lang.IllegalStateException:
Fragement no longer exists for key f1: index 3》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐