开发过程遇到一个问题,在使用ViewPager+Fragment加载多个fragment时,报出异常java.lang.IllegalStateException Fragment already added,现将解决方法记录如下。
错误代码:
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(VIEW_PAGER_DEFAULT_PAGE_NUMBER);
正是这两句代码的顺序导致了异常的抛出。原因是当调用setAdapter时,ViewPager调用了populate()去刷新UI,但是是按照默认的两页预加载的数量进行刷新,刷新完毕后调用了finishUpdate(),其中调用了commitNowAllowingStateLoss()方法;这之后调用的setOffscreenPageLimit()方法,其中也调用了populate()方法,这样我们就调用了两次populate(),也就间接调用了两次commitNowAllowingStateLoss()方法。代码如下:
public void setAdapter(PagerAdapter adapter) {
if (mAdapter != null) {
mAdapter.setViewPagerObserver(null);
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.setViewPagerObserver(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();
}
}
// Dispatch the change to any listeners
if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
}
}
}
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
而commitNowAllowingStateLoss()方法是立即执行,而不是等待 Activity 的 Handler 准备好了,如果多次调用,在FragmentManager调用addFragment()时,就会报出上述异常。代码如下:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mView == null) {
fragment.mHiddenChanged = false;
}
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
所以,解决方法就是按照正确的顺序编写代码,先setOffscreenPageLimit(),再setAdapter();
viewPager.setOffscreenPageLimit(VIEW_PAGER_DEFAULT_PAGE_NUMBER);
viewPager.setAdapter(adapter);