Android fragment源碼完全解析

緣起

一直以來(lái)荒揣,筆者對(duì)fragment相關(guān)的這一堆API,內(nèi)部具體干了哪些事情不是很清楚焊刹,所以經(jīng)常會(huì)用起來(lái)心里有點(diǎn)小疙瘩系任,不是那么踏實(shí)“槌危總想著花些時(shí)間去源碼中看看赋除,大概從2、3年前也偶爾會(huì)點(diǎn)進(jìn)去看看非凌,那時(shí)看的有點(diǎn)云里霧里的举农,不是特別明白,最近又點(diǎn)進(jìn)去看了看敞嗡,加上可以單步代碼颁糟,算是對(duì)其理解有所小成航背,于是乎趁著記憶還是新鮮的,特此記錄下棱貌。

涉及到的類(lèi)

和fragment相關(guān)的類(lèi)有以下幾個(gè):

什么是Fragment

Fragment是Android3.0引入的API玖媚,號(hào)稱(chēng)是為了解決屏幕碎片化和幫助重用代碼的構(gòu)造。中文翻譯為碎片婚脱、片段今魔,或者理解成子Act,相比Activity更輕量級(jí)障贸、更靈活错森,使用姿勢(shì)一般有以下2種:

  1. 靜態(tài)寫(xiě)在xml中,就和我們使用一般的widget一樣篮洁,如下:


    寫(xiě)在act的xml布局文件中
  2. 在代碼里動(dòng)態(tài)生成涩维,添加、刪除等等袁波,代碼如下:

通過(guò)代碼添加

參考fragment開(kāi)發(fā)文檔瓦阐。

什么是FragmentTransaction

它封裝了一系列對(duì)fragment的操作,并一次性執(zhí)行這些操作篷牌∷看眼它的API就能明白,全是各種add/remove/replace/show/hide等等操作娃磺。

什么是FragmentManager

它是和某個(gè)act相關(guān)聯(lián)的薄湿,并不是全局唯一的,而是每個(gè)act都有一個(gè)自己的FragmentManager偷卧,內(nèi)部有自己的狀態(tài)mCurState豺瘤,對(duì)應(yīng)外部act的生命周期狀態(tài)。它提供和act中fragment交互的API听诸。

示例代碼

private void addTestFragment() {
        FragmentManager fm = getFragmentManager();
        Fragment fragment = TestFragment.newInstance();
        FragmentTransaction ft = fm.beginTransaction();
//        ft.add(R.id.pf_base_fragment_container, fragment, "test_fragment");
        ft.replace(R.id.pf_base_fragment_container, fragment);
        ft.addToBackStack(null);
        ft.commit();
    }

之后的源碼解析都按照這段示例代碼來(lái)講坐求,源碼基于Android6.0。
在開(kāi)始分析之前晌梨,還得交代幾個(gè)事情桥嗤,如果我們仔細(xì)看了官方API會(huì)發(fā)現(xiàn)這里的FragmentManagerFragmentTransaction都是abstract的仔蝌,其真正的實(shí)現(xiàn)API對(duì)外當(dāng)然是沒(méi)有暴露泛领,不過(guò)我們看代碼知道分別是,如下圖:

FragmentManager.java
BackStackRecord.java

具體分析

通過(guò)FragmentTransaction這樣的API敛惊,我們可以一次執(zhí)行多個(gè)操作渊鞋,就像這樣:

ft.remove(R.id.fragment_container, fragment1);
ft.add(R.id.fragment_container, fragment2);
ft.hide(fragment3);
...等等還可以寫(xiě)很長(zhǎng)

它是怎么做到的呢?關(guān)鍵是其內(nèi)部的雙向鏈表結(jié)構(gòu):

Op的定義

接下來(lái)我們看下在此結(jié)構(gòu)上,F(xiàn)ragmentTransaction的一堆操作方法的實(shí)現(xiàn)锡宋,代碼如下:

void addOp(Op op) {
        // 典型的雙向鏈表插入操作
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        // 記錄這些動(dòng)畫(huà)信息
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }

    public FragmentTransaction add(Fragment fragment, String tag) {
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }

    public FragmentTransaction add(int containerViewId, Fragment fragment) {
        doAddOp(containerViewId, fragment, null, OP_ADD);
        return this;
    }

    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);
        return this;
    }

    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;

        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            // 記錄這些信息到fragment對(duì)象上
            fragment.mTag = tag;
        }

        if (containerViewId != 0) {
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            // 記錄這些信息到fragment對(duì)象上
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }

        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }

    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
        return replace(containerViewId, fragment, null);
    }

    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }

    public FragmentTransaction remove(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_REMOVE;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction hide(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_HIDE;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction show(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_SHOW;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction detach(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_DETACH;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction attach(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_ATTACH;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction setCustomAnimations(int enter, int exit) {
        return setCustomAnimations(enter, exit, 0, 0);
    }

    public FragmentTransaction setCustomAnimations(int enter, int exit,
            int popEnter, int popExit) {
        mEnterAnim = enter;
        mExitAnim = exit;
        mPopEnterAnim = popEnter;
        mPopExitAnim = popExit;
        return this;
    }

    public FragmentTransaction setTransition(int transition) {
        mTransition = transition;
        return this;
    }

    @Override
    public FragmentTransaction addSharedElement(View sharedElement, String name) {
        String transitionName = sharedElement.getTransitionName();
        if (transitionName == null) {
            throw new IllegalArgumentException("Unique transitionNames are required for all" +
                    " sharedElements");
        }
        if (mSharedElementSourceNames == null) {
            mSharedElementSourceNames = new ArrayList<String>();
            mSharedElementTargetNames = new ArrayList<String>();
        }
        mSharedElementSourceNames.add(transitionName);
        mSharedElementTargetNames.add(name);
        return this;
    }

    public FragmentTransaction setTransitionStyle(int styleRes) {
        mTransitionStyle = styleRes;
        return this;
    }

    public FragmentTransaction addToBackStack(String name) {
        if (!mAllowAddToBackStack) {
            throw new IllegalStateException(
                    "This FragmentTransaction is not allowed to be added to the back stack.");
        }
        mAddToBackStack = true;
        mName = name;
        return this;
    }

從源碼我們可以看出儡湾,各種不同操作方法內(nèi)部實(shí)際上只是個(gè)不同cmd的Op對(duì)象,通過(guò)雙向鏈表鏈起來(lái)了而已执俩,至于為什么要是雙向的呢徐钠,單鏈表不行嗎?那是因?yàn)镕ragment在act層面維護(hù)了一個(gè)回退棧役首,即如果你調(diào)用了ft.addToBackStack(null);這樣的代碼尝丐,那么當(dāng)你按下back鍵的時(shí)候不是直接結(jié)束當(dāng)前act,而是先從fragment棧里嘗試pop出來(lái)?xiàng)m數(shù)膄ragment宋税。它不僅要向前進(jìn)摊崭,還得支持向后退。
緊接著的是commit的源碼杰赛,如下:

public int commit() {
        return commitInternal(false);
    }

    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }

    int commitInternal(boolean allowStateLoss) {
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
            dump("  ", null, pw, null);
            pw.flush();
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

// FragmentManagerImpl#enqueueAction方法
/**
     * Adds an action to the queue of pending actions.
     *
     * @param action the action to add
     * @param allowStateLoss whether to allow loss of state information
     * @throws IllegalStateException if the activity has been destroyed
     */
    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

我們看到commit方法并沒(méi)有立即執(zhí)行這個(gè)動(dòng)作,只是入隊(duì)了action(通過(guò)mPendingActions.add(action);)矮台,系統(tǒng)會(huì)在下次event loop到來(lái)時(shí)執(zhí)行它乏屯。

FragmentManagerImpl的其他關(guān)鍵方法
Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };

/**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }
        
        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
            throw new IllegalStateException("Must be called from main thread of process");
        }

        boolean didSomething = false;

        while (true) {
            int numActions;
            
            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }
                
                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            
            mExecutingActions = true;
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }

        if (mHavePendingDeferredStart) {
            boolean loadersRunning = false;
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                if (f != null && f.mLoaderManager != null) {
                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                }
            }
            if (!loadersRunning) {
                mHavePendingDeferredStart = false;
                startPendingDeferredFragments();
            }
        }
        return didSomething;
    }

具體要執(zhí)行的action就是BackStackRecord#run方法,因?yàn)槲覀冏⒁獾剿约簩?shí)現(xiàn)了Runnable接口瘦赫,來(lái)看下其實(shí)現(xiàn):

public void run() {
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Run: " + this);
        }

        if (mAddToBackStack) {
            if (mIndex < 0) {
                throw new IllegalStateException("addToBackStack() called after commit()");
            }
        }

        bumpBackStackNesting(1);

        SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
        SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
        calculateFragments(firstOutFragments, lastInFragments);
        beginTransition(firstOutFragments, lastInFragments, false);

        Op op = mHead;
        while (op != null) {
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.addFragment(f, false);
                }
                break;
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i = 0; i < mManager.mAdded.size(); i++) {
                            Fragment old = mManager.mAdded.get(i);
                            if (FragmentManagerImpl.DEBUG) {
                                Log.v(TAG,
                                        "OP_REPLACE: adding=" + f + " old=" + old);
                            }
                            // 注意這個(gè)判斷辰晕,必須是同一個(gè)containerId里面的,換句話說(shuō)你如果是在2個(gè)不同的container里面做fragment的replace操作确虱,互相是不會(huì)影響的
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = op.exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) {
                                            Log.v(TAG, "Bump nesting of "
                                                    + old + " to " + old.mBackStackNesting);
                                        }
                                    }
                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
                                }
                            }
                        }
                    }
// 注意這個(gè)非空判斷含友,因?yàn)閞eplace自己的時(shí)候f會(huì)被置為null,相當(dāng)于什么也不會(huì)做的
                    if (f != null) { 
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    }
                }
                break;
                case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.removeFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_HIDE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.hideFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_SHOW: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.showFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_DETACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.exitAnim;
                    mManager.detachFragment(f, mTransition, mTransitionStyle);
                }
                break;
                case OP_ATTACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = op.enterAnim;
                    mManager.attachFragment(f, mTransition, mTransitionStyle);
                }
                break;
                default: {
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
            }

            op = op.next;
        }

        // 不帶fragment參數(shù)的狀態(tài)轉(zhuǎn)移
        mManager.moveToState(mManager.mCurState, mTransition,
                mTransitionStyle, true);

        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }

這個(gè)方法主要做的事情是向前遍歷Op雙向鏈表校辩,根據(jù)cmd的不同調(diào)用FragmentManagerImpl的以下方法窘问,特別注意下這里的OP_REPLACE cmd,看代碼我們知道它其實(shí)相當(dāng)于add/remove的組合宜咒,即如果有added會(huì)把所以已經(jīng)added了的先remove掉惠赫,再將新的add進(jìn)去,效果上是這樣故黑。

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.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            if (moveToStateNow) {
                moveToState(fragment);
            }
        }
    }
    
    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        // 加到回退棧中和不加的區(qū)別
        final boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            if (false) {
                // Would be nice to catch a bad remove here, but we need
                // time to test this to make sure we aren't crashes cases
                // where it is not a problem.
                if (!mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment not added: " + fragment);
                }
            }
            if (mAdded != null) {
                mAdded.remove(fragment);
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.mAdded = false;
            fragment.mRemoving = true;
// 沒(méi)有加到回退棧中則狀態(tài)退的更徹底儿咱,到狀態(tài)0而不是狀態(tài)1,區(qū)別就是最后的幾個(gè)destory相關(guān)的callback會(huì)不會(huì)被調(diào)用场晶。
            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
                    transition, transitionStyle, false);
        }
    }
    
    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "hide: " + fragment);
        if (!fragment.mHidden) {
            fragment.mHidden = true;
            if (fragment.mView != null) {
                Animator anim = loadAnimator(fragment, transition, false,
                        transitionStyle);
                if (anim != null) {
                    anim.setTarget(fragment.mView);
                    // Delay the actual hide operation until the animation finishes, otherwise
                    // the fragment will just immediately disappear
                    final Fragment finalFragment = fragment;
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            if (finalFragment.mView != null) {
                                finalFragment.mView.setVisibility(View.GONE);
                            }
                        }
                    });
                    setHWLayerAnimListenerIfAlpha(finalFragment.mView, anim);
                    anim.start();
                } else {
                    fragment.mView.setVisibility(View.GONE);
                }
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.onHiddenChanged(true);
        }
    }
    
    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "show: " + fragment);
        if (fragment.mHidden) {
            fragment.mHidden = false;
            if (fragment.mView != null) {
                Animator anim = loadAnimator(fragment, transition, true,
                        transitionStyle);
                if (anim != null) {
                    anim.setTarget(fragment.mView);
                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
                    anim.start();
                }
                fragment.mView.setVisibility(View.VISIBLE);
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.onHiddenChanged(false);
        }
    }
    
    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "detach: " + fragment);
        if (!fragment.mDetached) {
            fragment.mDetached = true;
            if (fragment.mAdded) {
                // We are not already in back stack, so need to remove the fragment.
                if (mAdded != null) {
                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
                    mAdded.remove(fragment);
                }
                if (fragment.mHasMenu && fragment.mMenuVisible) {
                    mNeedMenuInvalidate = true;
                }
                fragment.mAdded = false;
                moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
            }
        }
    }

    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "attach: " + fragment);
        if (fragment.mDetached) {
            fragment.mDetached = false;
            if (!fragment.mAdded) {
                if (mAdded == null) {
                    mAdded = new ArrayList<Fragment>();
                }
                if (mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment already added: " + fragment);
                }
                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
                mAdded.add(fragment);
                fragment.mAdded = true;
                if (fragment.mHasMenu && fragment.mMenuVisible) {
                    mNeedMenuInvalidate = true;
                }
                moveToState(fragment, mCurState, transition, transitionStyle, false);
            }
        }
    }

上面的run方法在對(duì)這一系列Op處理完后混埠,調(diào)用了下面的方法:
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);,對(duì)應(yīng)的源碼如下:

void moveToState(Fragment f) {
        moveToState(f, mCurState, 0, 0, false);
    }

    void moveToState(int newState, boolean always) {
        moveToState(newState, 0, 0, always);
    }
    
    // 這里調(diào)用的是這個(gè)方法
    void moveToState(int newState, int transit, int transitStyle, boolean always) {
        if (mHost == null && newState != Fragment.INITIALIZING) {
            throw new IllegalStateException("No activity");
        }

        if (!always && mCurState == newState) {
            return;
        }

        mCurState = newState;
        if (mActive != null) {
            boolean loadersRunning = false;
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                if (f != null) {
                    // fragment狀態(tài)改變的方法诗轻,其各種生命周期回調(diào)也在這里調(diào)用钳宪,是最重要的一個(gè)方法!!使套!
                    moveToState(f, newState, transit, transitStyle, false);
                    if (f.mLoaderManager != null) {
                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                    }
                }
            }

            if (!loadersRunning) {
                startPendingDeferredFragments();
            }

            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
                mHost.onInvalidateOptionsMenu();
                mNeedMenuInvalidate = false;
            }
        }
    }

這里我們需要對(duì)mManager.mCurState這個(gè)值的變化細(xì)說(shuō)下罐呼,其初始值是這樣的:

int mCurState = Fragment.INITIALIZING;

Fragment的所有可能狀態(tài):

static final int INVALID_STATE = -1;   // Invalid state used as a null value.
    static final int INITIALIZING = 0;     // Not yet created.
    static final int CREATED = 1;          // Created.
    static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
    static final int STOPPED = 3;          // Fully created, not started.
    static final int STARTED = 4;          // Created and started, not resumed.
    static final int RESUMED = 5;          // Created started and resumed.

FragmentManager中有一堆public void dispatchxxx這樣的方法,如下:

public void dispatchCreate() {
        mStateSaved = false;
        moveToState(Fragment.CREATED, false);
    }
    
    public void dispatchActivityCreated() {
        mStateSaved = false;
        moveToState(Fragment.ACTIVITY_CREATED, false);
    }
    
    public void dispatchStart() {
        mStateSaved = false;
        moveToState(Fragment.STARTED, false);
    }
    
    public void dispatchResume() {
        mStateSaved = false;
        moveToState(Fragment.RESUMED, false);
    }
    
    public void dispatchPause() {
        moveToState(Fragment.STARTED, false);
    }
    
    public void dispatchStop() {
        moveToState(Fragment.STOPPED, false);
    }
    
    public void dispatchDestroyView() {
        moveToState(Fragment.CREATED, false);
    }

    public void dispatchDestroy() {
        mDestroyed = true;
        execPendingActions();
        moveToState(Fragment.INITIALIZING, false);
        mHost = null;
        mContainer = null;
        mParent = null;
    }

而這些正好是和act的生命周期對(duì)應(yīng)起來(lái)侦高,也就是說(shuō)這些方法是隨著act進(jìn)入到不同的生命周期而被調(diào)用的嫉柴,即mCurState的值是被這些方法觸發(fā)設(shè)置的。比如act進(jìn)入到了Resume狀態(tài)奉呛,那么FragmentManagerImpl.mCurState也就等于Fragment.RESUMED计螺。

最后,我們分析下fragment狀態(tài)轉(zhuǎn)移最重要的一個(gè)方法瞧壮,如下:

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        if (DEBUG && false) Log.v(TAG, "moveToState: " + f
            + " oldState=" + f.mState + " newState=" + newState
            + " mRemoving=" + f.mRemoving + " Callers=" + Debug.getCallers(5));

        // Fragments that are not currently added will sit in the onCreate() state.
        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
            newState = Fragment.CREATED;
        }
        if (f.mRemoving && newState > f.mState) {
            // While removing a fragment, we can't change it to a higher state.
            newState = f.mState;
        }
        // Defer start if requested; don't allow it to move to STARTED or higher
        // if it's not already started.
        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
            newState = Fragment.STOPPED;
        }
        if (f.mState < newState) {
            // For fragments that are created from a layout, when restoring from
            // state we don't want to allow them to be created until they are
            // being reloaded from the layout.
            if (f.mFromLayout && !f.mInLayout) {
                return;
            }
            if (f.mAnimatingAway != null) {
                // The fragment is currently being animated...  but!  Now we
                // want to move our state back up.  Give up on waiting for the
                // animation, move to whatever the final state should be once
                // the animation is done, and then we can proceed from there.
                f.mAnimatingAway = null;
                moveToState(f, f.mStateAfterAnimating, 0, 0, true);
            }
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                    if (f.mSavedFragmentState != null) {
                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                FragmentManagerImpl.VIEW_STATE_TAG);
                        f.mTarget = getFragment(f.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG);
                        if (f.mTarget != null) {
                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                        }
                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                        if (!f.mUserVisibleHint) {
                            f.mDeferStart = true;
                            if (newState > Fragment.STOPPED) {
                                newState = Fragment.STOPPED;
                            }
                        }
                    }
                    f.mHost = mHost;
                    f.mParentFragment = mParent;
                    f.mFragmentManager = mParent != null
                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                    f.mCalled = false;
                    f.onAttach(mHost.getContext());
                    if (!f.mCalled) {
                        throw new SuperNotCalledException("Fragment " + f
                                + " did not call through to super.onAttach()");
                    }
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    }

                    if (!f.mRetaining) {
                        f.performCreate(f.mSavedFragmentState);
                    }
                    f.mRetaining = false;
                    if (f.mFromLayout) {
                        // For fragments that are part of the content view
                        // layout, we need to instantiate the view immediately
                        // and the inflater will take care of adding it.
                        f.mView = f.performCreateView(f.getLayoutInflater(
                                f.mSavedFragmentState), null, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mView.setSaveFromParentEnabled(false);
                            if (f.mHidden) f.mView.setVisibility(View.GONE);
                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                        }
                    }
                case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    throwException(new IllegalArgumentException(
                                            "No view found for id 0x"
                                            + Integer.toHexString(f.mContainerId) + " ("
                                            + f.getResources().getResourceName(f.mContainerId)
                                            + ") for fragment " + f));
                                }
                            }
                            f.mContainer = container;
                            f.mView = f.performCreateView(f.getLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                                    Animator anim = loadAnimator(f, transit, true,
                                            transitionStyle);
                                    if (anim != null) {
                                        anim.setTarget(f.mView);
                                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
                                        anim.start();
                                    }
                                    container.addView(f.mView);
                                }
                                if (f.mHidden) f.mView.setVisibility(View.GONE);
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                            }
                        }

                        f.performActivityCreated(f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.restoreViewState(f.mSavedFragmentState);
                        }
                        f.mSavedFragmentState = null;
                    }
                case Fragment.ACTIVITY_CREATED:
                case Fragment.STOPPED:
                    if (newState > Fragment.STOPPED) {
                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                        f.performStart();
                    }
                case Fragment.STARTED:
                    if (newState > Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                        f.mResumed = true;
                        f.performResume();
                        // Get rid of this in case we saved it and never needed it.
                        f.mSavedFragmentState = null;
                        f.mSavedViewState = null;
                    }
            }
        } else if (f.mState > newState) {
            switch (f.mState) {
                case Fragment.RESUMED:
                    if (newState < Fragment.RESUMED) {
                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                        f.performPause();
                        f.mResumed = false;
                    }
                case Fragment.STARTED:
                    if (newState < Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                        f.performStop();
                    }
                case Fragment.STOPPED:
                case Fragment.ACTIVITY_CREATED:
                    if (newState < Fragment.ACTIVITY_CREATED) {
                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
                        if (f.mView != null) {
                            // Need to save the current view state if not
                            // done already.
                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
                                saveFragmentViewState(f);
                            }
                        }
                        f.performDestroyView();
                        if (f.mView != null && f.mContainer != null) {
                            Animator anim = null;
                            if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
                                anim = loadAnimator(f, transit, false,
                                        transitionStyle);
                            }
                            if (anim != null) {
                                final ViewGroup container = f.mContainer;
                                final View view = f.mView;
                                final Fragment fragment = f;
                                container.startViewTransition(view);
                                f.mAnimatingAway = anim;
                                f.mStateAfterAnimating = newState;
                                anim.addListener(new AnimatorListenerAdapter() {
                                    @Override
                                    public void onAnimationEnd(Animator anim) {
                                        container.endViewTransition(view);
                                        if (fragment.mAnimatingAway != null) {
                                            fragment.mAnimatingAway = null;
                                            moveToState(fragment, fragment.mStateAfterAnimating,
                                                    0, 0, false);
                                        }
                                    }
                                });
                                anim.setTarget(f.mView);
                                setHWLayerAnimListenerIfAlpha(f.mView, anim);
                                anim.start();

                            }
                            f.mContainer.removeView(f.mView);
                        }
                        f.mContainer = null;
                        f.mView = null;
                    }
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        if (mDestroyed) {
                            if (f.mAnimatingAway != null) {
                                // The fragment's containing activity is
                                // being destroyed, but this fragment is
                                // currently animating away.  Stop the
                                // animation right now -- it is not needed,
                                // and we can't wait any more on destroying
                                // the fragment.
                                Animator anim = f.mAnimatingAway;
                                f.mAnimatingAway = null;
                                anim.cancel();
                            }
                        }
                        if (f.mAnimatingAway != null) {
                            // We are waiting for the fragment's view to finish
                            // animating away.  Just make a note of the state
                            // the fragment now should move to once the animation
                            // is done.
                            f.mStateAfterAnimating = newState;
                            newState = Fragment.CREATED;
                        } else {
                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                            if (!f.mRetaining) {
                                f.performDestroy();
                            }

                            f.mCalled = false;
                            f.onDetach();
                            if (!f.mCalled) {
                                throw new SuperNotCalledException("Fragment " + f
                                        + " did not call through to super.onDetach()");
                            }
                            if (!keepActive) {
                                if (!f.mRetaining) {
                                    makeInactive(f);
                                } else {
                                    f.mHost = null;
                                    f.mParentFragment = null;
                                    f.mFragmentManager = null;
                                    f.mChildFragmentManager = null;
                                }
                            }
                        }
                    }
            }
        }
        
        f.mState = newState;
    }

這個(gè)方法最終會(huì)將FragmentManager的狀態(tài)賦值給fragment登馒,另外這個(gè)方法會(huì)根據(jù)不同的state調(diào)用各種onAttach, Fragment.performXXX,進(jìn)而調(diào)到用戶(hù)自己override的fragment的各種生命周期方法咆槽,比如onCreate陈轿、onCreateView等等。

總結(jié)

  1. show/hideFragment只是改變fragment根View的visibility秦忿,最多帶上個(gè)動(dòng)畫(huà)效果麦射,另外只有本身是hidden的fragment,調(diào)用show才起作用灯谣,否則沒(méi)用的潜秋,fragment.onHiddenChanged會(huì)被觸發(fā);其次不會(huì)有生命周期callback觸發(fā)胎许,當(dāng)然了這些操作的前提是已經(jīng)被add了的fragment峻呛;

  2. addFragment的時(shí)候,不管加不加入回退棧都一樣辜窑,經(jīng)歷的生命周期如下:onAttach钩述、onCreate、onCreateView谬擦、onActivityCreate切距、onStart、onResume惨远;

  3. removeFragment的時(shí)候谜悟,經(jīng)歷的生命周期如下:onPause、onStop北秽、onDestroyView葡幸,如果不加回退棧還會(huì)繼續(xù)走
    onDestroy、onDetach贺氓;remove的時(shí)候不僅從mAdded中移除fragment蔚叨,也從mActive中移除了;


    FragmentManagerImpl中關(guān)鍵字段申明
  4. attach/detachFragment的前提都是已經(jīng)add了的fragment,其生命周期回調(diào)不受回退棧影響蔑水。attach的時(shí)候onCreateView邢锯、onActivityCreate、onStart搀别、onResume會(huì)被調(diào)用丹擎;detach的時(shí)候onPause、onStop歇父、onDestroyView會(huì)被調(diào)用蒂培,onDestroy、onDetach不會(huì)被調(diào)用榜苫;對(duì)應(yīng)的fragment只是從mAdded中移除了护戳;

  5. remove、detachFragment的時(shí)候垂睬,當(dāng)FragmentManagerImpl.makeInactive()被調(diào)用的話媳荒,fragment就變成了一個(gè)空殼,里面絕大部分字段都會(huì)被置空驹饺,注意只是系統(tǒng)內(nèi)部自己管理的字段肺樟,假如你在自己的fragment子類(lèi)中引入了新的字段,當(dāng)你重用這些類(lèi)的對(duì)象時(shí)要自己處理這種情況(即系統(tǒng)不會(huì)reset你自己造的字段)逻淌,代碼如下:

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);
        mHost.inactivateFragment(f.mWho);
        f.initState();
    }

// Fragment.initState方法
/**
     * Called by the fragment manager once this fragment has been removed,
     * so that we don't have any left-over state if the application decides
     * to re-use the instance.  This only clears state that the framework
     * internally manages, not things the application sets.
     */
    void initState() {
        mIndex = -1;
        mWho = null;
        mAdded = false;
        mRemoving = false;
        mResumed = false;
        mFromLayout = false;
        mInLayout = false;
        mRestored = false;
        mBackStackNesting = 0;
        mFragmentManager = null;
        mChildFragmentManager = null;
        mHost = null;
        mFragmentId = 0;
        mContainerId = 0;
        mTag = null;
        mHidden = false;
        mDetached = false;
        mRetaining = false;
        mLoaderManager = null;
        mLoadersStarted = false;
        mCheckedForLoaderManager = false;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疟暖,隨后出現(xiàn)的幾起案子卡儒,更是在濱河造成了極大的恐慌,老刑警劉巖俐巴,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骨望,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡欣舵,警方通過(guò)查閱死者的電腦和手機(jī)擎鸠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缘圈,“玉大人劣光,你說(shuō)我怎么就攤上這事≡惆眩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)话速。 經(jīng)常有香客問(wèn)我扶镀,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任数苫,我火速辦了婚禮聪舒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虐急。我一直安慰自己箱残,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布戏仓。 她就那樣靜靜地躺著疚宇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赏殃。 梳的紋絲不亂的頭發(fā)上敷待,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音仁热,去河邊找鬼榜揖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抗蠢,可吹牛的內(nèi)容都是我干的举哟。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼迅矛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妨猩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起秽褒,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤壶硅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后销斟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體庐椒,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年蚂踊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了约谈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犁钟,死狀恐怖棱诱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情特纤,我是刑警寧澤军俊,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站捧存,受9級(jí)特大地震影響粪躬,放射性物質(zhì)發(fā)生泄漏担败。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一镰官、第九天 我趴在偏房一處隱蔽的房頂上張望提前。 院中可真熱鬧,春花似錦泳唠、人聲如沸狈网。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拓哺。三九已至,卻和暖如春脖母,著一層夾襖步出監(jiān)牢的瞬間士鸥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工谆级, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烤礁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓肥照,卻偏偏與公主長(zhǎng)得像脚仔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舆绎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容