修复一个ViewPager+Fragment报的java.lang.IllegalStateException Fragment already added问题

开发过程遇到一个问题,在使用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();
        }
        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);

 

 

你可能感兴趣的:(Android开发)