Android之FragmentManager add機(jī)制的變遷

起因

最近在把項目support庫從23.1.1升級至23.4.0時,突然發(fā)現(xiàn)有些地方會莫名的找不到"已經(jīng)"add的fragment,從而導(dǎo)致了空指針引用異常。郁悶的我就仔細(xì)對比了23.1.1和23.4.0源碼,發(fā)現(xiàn)原來是google悄悄地把FragmentManager add()機(jī)制更改了,真是坑爹-,-。

探究

如下是一段很平常的代碼:


//In Activity.java
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    getSupportFragmentManager()
        .beginTransaction()
        .add(android.R.id.content, fragmentA, fragmentA.TAG)
        .add(android.R.id.content, fragmentB, fragmentB.TAG)
        .commit();
    }

//In FragmentA.java
     @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            mFragmentB =(FragmentB)getFragmentManager()
                   .findFragmentByTag(FragmentB.TAG);
        mFragmentB.performSomeAction();
       ... ...
    }

在23.1.1之前FragmentManager的add機(jī)制:

//STEP0: 記錄已經(jīng)添加和有效的fragment集合
    ArrayList<Fragment> mAdded;
    ArrayList<Fragment> mActive;
    ... ...
    public void run() {
    ... ...
//STEP1: 根據(jù)之前的add順序,從mHead中取出fragment并依次放入lastInFragments中,為之后的transition做準(zhǔn)備。
    calculateFragments(firstOutFragments,lastInFragments);

//STEP2: 再根據(jù)lastInFragments集合統(tǒng)計出fragments的過渡狀態(tài)(因?yàn)閮Hadd操作,所以firstOutFragments為空)
    state=beginTransition(firstOutFragments,lastInFragments, false);
    ... ...
//STEP3:依次將mHead鏈表中的fragment加入至mAdded和mActive中,但不會更改fragment的狀態(tài)(即不會觸發(fā)fragment的初始化操作)。
    while (op != null) {
            int enterAnim = state != null ? 0 : op.enterAnim;
            int exitAnim = state != null ? 0 : op.exitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;
                ... ...
            }
    }
    
//STEP4: 最后更新mActive集合中所有fragment的狀態(tài)(對于add操作而言即是觸發(fā)了fragment的初始化操作)
    mManager.moveToState(mManager.mCurState
                            , transition, transitionStyle, true);
    ... ...
}

而在23.4.0版本的support庫中,它初始化fragment的時機(jī)發(fā)生了變化,主要變化是在上述的STEP1中。
在23.4.0的STEP1中,除了根據(jù)之前的add順序,從mHead中取出fragment并依次放入lastInFragments中,為之后的transition做準(zhǔn)備。并且將新add的fragment放入mActive中,同時更新fragment的狀態(tài)(即觸發(fā)了fragment的初始化操作)。

// 23.1.1版本:
    private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) {
        if (fragment != null) {
            int containerId = fragment.mContainerId;
            if (containerId != 0) {
                fragments.put(containerId, fragment);
            }
        }
    }
    
// 23.4.0版本:
    private void setLastIn(... , Fragment fragment) {
        if (fragment != null) {
             ... ...
            if (fragment.mState < Fragment.CREATED 
                    && mManager.mCurState >= Fragment.CREATED) {
                mManager.makeActive(fragment);
                mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
            }
        }
    }

因?yàn)橛|發(fā)fragment初始化時機(jī)發(fā)生了改變,所以當(dāng)FragmentB在FragmentA之后add,此時在FragmentA的onCreate方法中通過findFragmentByTag找FragmentB時,必然找到的為空(即mAdded和mActive兩個集合中都無法找到),因?yàn)榇藭rFragmentB根本還未來得及被添加至mAdded或mActive集合中。

    public Fragment findFragmentByTag(String tag) {
        if (mAdded != null && tag != null) {
            // First look through added fragments.
            for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }
        if (mActive != null && tag != null) {
            // Now for any known fragment.
            for (int i=mActive.size()-1; i>=0; i--) {
                Fragment f = mActive.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }
        return null;
     }

結(jié)語

所以謹(jǐn)記,如果兩個fragment之間有調(diào)用關(guān)系的話,添加他們時一定要優(yōu)先add被調(diào)用的fragment(即例子中的FragmentB),以方support庫升級后引起空指針異常。


歡迎查看 個人博客.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容