getSupportFragmentManager().getFragments() 抛出空指针异常

背景很简单,在一个FragmentActivity上显示我想要的fragment。
每次显示Fragment的时候我都调用一个函数:

public static boolean showFragmentInActivity(SdkFragment frag, String tag, SdkActivity actv) {
    if (frag == null || actv == null || actv.getSupportFragmentManager() == null) {
        return false;
    }
    FragmentTransaction transaction = actv.getSupportFragmentManager().beginTransaction();
    if (transaction == null) {
        return false;
    }
    List fragments = actv.getSupportFragmentManager().getFragments();
    for (int i = 0; fragments != null && i < fragments.size(); i++) {     
        transaction.remove(fragments.get(i));
    }
    transaction.add(frag, tag);
    transaction.commitAllowingStateLoss();
    return true;
}

该函数是找到该Activity上所有的Fragments,移除这些Fragments,然后加入我要显示的Fragment,将事务提交。
crash重现步骤:
在一个FragmentActivity上我先是显示了两个DialogFragment(标识为A和B,直接采用DialogFragment.show(…)方法),然后我将A和B都dismiss掉,显示另一个DialogFragment(标识为C,调用上述showFragmentInActivity的方法),然后我再dismiss掉C,显示另一个DialogFragment(标识为D,调用上述showFragmentInActivity的方法),此时抛出了如下的异常:
getSupportFragmentManager().getFragments() 抛出空指针异常_第1张图片
该错误是因为FragmentTransaction的add、remove、detach、attach函数如果传入的参数为null的话,在将事务提交的话,会抛出该异常。
在上述的函数中,我对传入的Fragment已经做过判空检查,所以add函数的参数不可能为空,只可能为remove函数的参数为空。remove的参数我是从getSupportFragmentManager().getFragments()中获取的,怎么可能为空呢。
查看了下getFragments的源代码:

ArrayList mActive;
@Override
public List getFragments() {
    return mActive;
}

从上述代码中看出mActive是用来存当前活跃(Active)的fragment的列表。
我又找到两段代码:

void makeActive(Fragment f) {
    if (f.mIndex >= 0) {
        return;
    }

    if (mAvailIndices == null || mAvailIndices.size() <= 0) {
        if (mActive == null) {
            mActive = new ArrayList<Fragment>();
        }
        f.setIndex(mActive.size(), mParent);
        mActive.add(f);

    } else {
        f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
        mActive.set(f.mIndex, f);
    }
    if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}

void makeInactive(Fragment f) {
    if (f.mIndex < 0) {
        return;
    }

    if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
    mActive.set(f.mIndex, null);
    if (mAvailIndices == null) {
        mAvailIndices = new ArrayList<Integer>();
    }
    mAvailIndices.add(f.mIndex);
    mActivity.invalidateSupportFragment(f.mWho);
    f.initState();
}

这两个函数是源代码内部调用的,我猜想makeActive函数应该是当一个fragment处于active的时候,将该fragment加入该列表中。如果一个Fragment不处于Active状态的时候,将该fragment所在的位置置为空。
非常重要的一点是,我在源代码中没有找到任何一处调用mActive.remove(…),只有mActive.add(…),这说明该列表只有加入,没有删除,只是置为空而已。所以难怪调用getFragments()函数返回的时候,可能存在空的元素(ArrayList是允许空的元素存在的)

解决办法就是在你从fragments中取出的时候,做一个判空就好了。

for (int i = 0; fragments != null && i < fragments.size(); i++) {
    /* 这里遇到一个exception,获取fragments的时候,返回的size为2,但是第二个fragment为空
     * 如果remove、add、detach、attach方法的参数为空,则会抛出如下的异常:
     * java.lang.NullPointerException
     * at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:591).....
     */
    if (fragments.get(i) != null) {
        transaction.remove(fragments.get(i));
    }
}

你可能感兴趣的:(android)