IllegalStateException:<MyFragment>当前不在FragmentManager中

我知道这听起来像是FragmentStatePagerAdapter IllegalStateException的重复:<MyFragment>目前不在FragmentManager中,但他的解决scheme与我的情况无关。

我很less遇到以下崩溃:

java.lang.RuntimeException:无法暂停活动{MyActivity}:

由于:java.lang.IllegalStateException:Fragment MyFragment {40648258 id = 0x7f070051}目前不在android.support.v4.app.FragmentManagerImpl.putFragment(MT:516)的FragmentManager中android.support.v4.app.FragmentStatePagerAdapter .saveState(MT:185)在android.support.v4.view.ViewPager.onSaveInstanceState(MT:881)

在android.app.Activity.onSaveInstanceState(Activity.java:1138)com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1522)android.view.View.saveHierarchyState(View.java:6238) )在MyActivity.onSaveInstanceState(MT:336)处的android.support.v4.app.FragmentActivity.onSaveInstanceState(MT:480)

这似乎是我从FragmentStatePagerAdapter无法理解的奇怪的代码:

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

它看起来像适配器从mFragments获取我的Fragment ,但不能将其状态添加到FragmentManager

我找不到在我的testing设备上重新创build的任何方法,只有从一些用户那里得到这个。

我正在使用支持包v4。

任何帮助? 谢谢。

FragmentStatePagerAdapter是一个可怕的代码,充满了谷歌的错误,所以我使用这段代码来解决这个特定的崩溃:

  @Override public void destroyItem(ViewGroup container, int position, Object object) { // Yet another bug in FragmentStatePagerAdapter that destroyItem is called on fragment that hasnt been added. Need to catch try { super.destroyItem(container, position, object); } catch (IllegalStateException ex) { ex.printStackTrace(); } } 

当在ViewPager上设置FragmentStatePagerAdapter时,可能会发生这种情况,由于在ViewPager发生布局之前活动暂停,碎片被填充,但尚未添加到FragmentManager。 这可以通过从onCreate启动另一个Activity来进行可靠的命中,同时Adapter也在onCreate中设置。 在这种情况下,最好暂缓设置Adapter,并将其设置为ActivityResult。 看到这个问题: https : //code.google.com/p/android/issues/detail?id = 77285

我还提交了一个补丁来检查在试图保存状态之前添加的碎片。

如果您有片段层次结构,请使用getChildFragmentManager()而不是getFragmentManager() 。 例如。 如果你有片段寻呼机。

我遇到了确切的例外。

在我的情况下,我有几个片段由FragmentPagerStateAdapterpipe理,但有时片段将被切换到位置。

我重写getItemPosition()并返回新的位置,并调用notifyDataSetChanged()刷新ViewPager。 一切正常,但有时当我离开ViewPager发生崩溃。

经过几个小时的挖掘,我发现FragmentPagerStateAdapter中有一个bug。

适配器不仅可以caching状态,还可以caching它认为“活动”的碎片

 private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); private ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); 

但是它不检查碎片的位置是否失效或移动。

这就是它如何得到这个例外:

我在ViewPager和Adapter中有A,B,C三个片段。

2.我在适配器中将位置切换到B,A,C,并调用notifyDataSetChanged()。

3.ViewPager按新的顺序重新排列片段。 但是mFragmentscaching仍然是{A,B,C}

4.Swipe几页,ViewPager会要求适配器销毁第一个片段。 那么片段B,而不是A,在caching中设置为空。 现在mFragments是{null,A,C}。

5.保留ViewPager,并触发onSaveInstanceState。 mFragments中的所有片段都被认为是活动的,putFragment()被调用,直到FragmentManagerImpl发现A不在其中,并抛出一个Exception。

所以这里是我不那么温和的解决scheme:

1.复制整个FragmentPagerStateAdapter源代码。

2.覆盖notifyDataSetChanged()方法重新排列caching,如下所示。

 @Override public void notifyDataSetChanged() { List<Fragment> oldFragments = new ArrayList<>(mFragments); List<Fragment.SavedState> oldStates = new ArrayList<>(mSavedState); for (int i = 0; i < getCount(); i++) { if (i < mFragments.size()) { Fragment f = mFragments.get(i); if (f != null) { int newPosition = getItemPosition(f); if (newPosition == POSITION_UNCHANGED || newPosition == i) { } else if (newPosition == POSITION_NONE) { if (i < mSavedState.size()) { mSavedState.set(i, null); } mFragments.set(i, null); } else { while (i >= mFragments.size()) { mFragments.add(null); } if (oldStates.size() > i) { mSavedState.set(newPosition, oldStates.get(i)); } mFragments.set(newPosition, oldFragments.get(i)); } } else { /* * No Fragment but that's possible there's savedState and position has * changed. */ } } } super.notifyDataSetChanged(); } 

希望对你们中的一些人有用!

如果你的ViewPager是在一个片段(而不是一个活动)内:

mViewPager.setAdapter(new MyFragmentStatePagerAdapter(get Child FragmentManager()));

使用以下内容

 final FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(f, key); 

代替

 mFragmentManager.putFragment(state, key, f); 

并明确传递包

以供参考,

http://developer.android.com/reference/android/app/FragmentTransaction.html#add%28android.app.Fragment,%20java.lang.String%29

你是从自己的代码调用FragmentStatePagerAdapter.instantiateItem()吗? 一般来说,ViewPager应该是唯一调用这个方法的客户端,但是,很容易find依赖于这个方法的其他限制的解决方法。

由于FragmentStatePagerAdapter工作原理,我猜想适配器已经实例化了Fragment,将它添加到了Fragment的内部集合中,甚至开始了一个事务来将Fragment添加到FragmentManager中,但是这个碎片事务没有被提交或执行。 这正是instantiateItem()在尚未实例化请求的Fragment时所做的。

另一种方法, FragmentStatePagerAdapter.finishUpdate()负责提交事务以及执行未决事务。 finishUpdate()必须在instantiateItem()之后调用,否则Fragment可能不会被添加到FragmentManager中。

尝试这样的事情:

 // Get the current Fragment presented by the ViewPager. pagerAdapter.startUpdate(viewPager); Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem()); pagerAdapter.finishUpdate(viewPager); return fragment; 

startUpdate()在API级别19中有一个空实现。

你可以尝试使用mFragmentManager.add();

在Activity中写入onCreate()方法:

 pager = (ViewPager) findViewById(R.id.pager); adapter = new SwipePagerAdapter(getSupportFragmentManager()); pageOneFragment = new PageOneFragment(); adapter.addFragment(pageOneFragment); 

适配器代码:

 public class SwipePagerAdapter extends FragmentStatePagerAdapter 

{private final ArrayList mFragments = new ArrayList();

 public SwipePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments.size(); } public void addFragment(Fragment fragment) { mFragments.add(fragment); notifyDataSetChanged(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); } @Override public CharSequence getPageTitle(int position) { return super.getPageTitle(position); } 

}

尝试使用

 use fragmentTransaction.add() 

ViewPager中的片段是固定的,而不是尝试replace适配器中的片段,尝试给出不同的片段集合并更改notifyDataSet,或者利用FrameLayout在视图分页器选项卡的当前片段上显示另一个片段。

有我的解决scheme工作:

滑动手势与ViewPager一起应用于片段级别,禁用默认滑动

仔细检查您的activity是否已经实现了onSaveInstanceStateonRestoreInstanceState ,并validation它们是否正确保存并加载了碎片的状态。