Android 進階17:Fragment FragmentManager FragmentTransaction 深入理解

Love Lenka發表於2017-09-29

日常開發中我們經常使用 Fragment 管理佈局,使用起來非常方便,但是在簡單的 API 背後隱藏了什麼操作,很多人恐怕不瞭解。

如果你回答不出這些問題,那這篇文章可能就對你有些幫助:

  • Fragment FragmentManager FragmentTransaction 的關係和作用
  • Fragment 如何實現佈局的新增替換
  • 巢狀 Fragment 的原理

讀完本文你將瞭解:

注意,這裡分析的是 support-fragment-25.3.1-source.jar" 的support.v4.app.Fragment 及相關類。

Fragment 的使用

Fragment 的使用大家應該都熟悉,這裡舉個例子,要實現這樣的類似餓了麼點餐效果:

這裡寫圖片描述

介面醜了點,但意思是差不多的哈,,左邊一個列表,點選後切換右邊的佈局。我們就可以使用 Fragment 來實現。

實現也很簡單,建立一個的佈局,然後在 Activity 裡點選時替換 Fragment。

mFragmentManager = getSupportFragmentManager();
mFragmentManager.beginTransaction()
    .replace(R.id.fl_content, fragment)
    .commitAllowingStateLoss();

程式碼很簡單,核心就三步:

  1. 建立 Fragment
  2. 獲取 FragmentManager
  3. 呼叫事務,新增、替換

我們一步步來了解這背後的故事。

Fragment 大家應該比較熟悉,放到最後。

先來看看 FragmentManager

FragmentManager

public abstract class FragmentManager {...}

FragmentManager 是一個抽象類,定義了一些和 Fragment 相關的操作和內部類/介面。

定義的操作

FragmentManager 中定義的方法如下:

//開啟一系列對 Fragments 的操作
public abstract FragmentTransaction beginTransaction();

//FragmentTransaction.commit() 是非同步執行的,如果你想立即執行,可以呼叫這個方法
public abstract boolean executePendingTransactions();

//根據 ID 找到從 XML 解析出來的或者事務中新增的 Fragment
//首先會找新增到 FragmentManager 中的,找不到就去回退棧裡找
public abstract Fragment findFragmentById(@IdRes int id);

//跟上面的類似,不同的是使用 tag 進行查詢
public abstract Fragment findFragmentByTag(String tag);

//彈出回退棧中棧頂的 Fragment,非同步執行的
public abstract void popBackStack();

//立即彈出回退棧中棧頂的,直接執行哦
public abstract boolean popBackStackImmediate();

//返回棧頂符合名稱的,如果傳入的 name 不為空,在棧中間找到了 Fragment,那將彈出這個 Fragment 上面的所有 Fragment
//有點類似啟動模式的 singleTask 的感覺
//如果傳入的 name 為 null,那就和 popBackStack() 一樣了
//非同步執行
public abstract void popBackStack(String name, int flags);

//同步版的上面
public abstract boolean popBackStackImmediate(String name, int flags);

//和使用 name 查詢、彈出一樣 
//不同的是這裡的 id 是 FragmentTransaction.commit() 返回的 id
public abstract void popBackStack(int id, int flags);
//你懂得
public abstract boolean popBackStackImmediate(int id, int flags);

//獲取回退棧中的元素個數
public abstract int getBackStackEntryCount();
//根據索引獲取回退棧中的某個元素
public abstract BackStackEntry getBackStackEntryAt(int index);

//新增或者移除一個監聽器
public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);

//還定義了將一個 Fragment 例項作為引數傳遞
public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
public abstract Fragment getFragment(Bundle bundle, String key);

//獲取 manager 中所有新增進來的 Fragment
public abstract List<Fragment> getFragments();

可以看到,定義的方法有很多是非同步執行的,後面看看它究竟是如何實現的非同步。

內部類/介面:

  • BackStackEntry:Fragment 後退棧中的一個元素
  • onBackStackChangedListener:後退棧變動監聽器
  • FragmentLifecycleCallbacks: FragmentManager 中的 Fragment 生命週期監聽
//後退棧中的一個元素
public interface BackStackEntry {
    //棧中該元素的唯一標識
    public int getId();

    //獲取 FragmentTransaction#addToBackStack(String) 設定的名稱
    public String getName();

    @StringRes
    public int getBreadCrumbTitleRes();
    @StringRes
    public int getBreadCrumbShortTitleRes();
    public CharSequence getBreadCrumbTitle();
    public CharSequence getBreadCrumbShortTitle();
}

可以看到 BackStackEntry 的介面比較簡單,關鍵資訊就是 ID 和 Name。

//在 Fragment 回退棧中有變化時回撥
public interface OnBackStackChangedListener {
    public void onBackStackChanged();
}
//FragmentManager 中的 Fragment 生命週期監聽
    public abstract static class FragmentLifecycleCallbacks {
        public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
        public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
        public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
                Bundle savedInstanceState) {}
        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
                Bundle savedInstanceState) {}
        public void onFragmentStarted(FragmentManager fm, Fragment f) {}
        public void onFragmentResumed(FragmentManager fm, Fragment f) {}
        public void onFragmentPaused(FragmentManager fm, Fragment f) {}
        public void onFragmentStopped(FragmentManager fm, Fragment f) {}
        public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
        public void onFragmentDetached(FragmentManager fm, Fragment f) {}
    }
}

熟悉 Fragment 生命週期的同學一定覺得很面熟,這個介面就是為我們提供一個 FragmentManager 所有 Fragment 生命週期變化的回撥。

小結:

可以看到,FragmentManager 是一個抽象類,它定義了對一個 Activity/Fragment 中 新增進來的 Fragment 列表Fragment 回退棧的操作、管理。

實現類 FragmentManagerImpl

FragmentManager 定義的任務是由 FragmentManagerImpl 實現的。

主要成員:

final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {

    ArrayList<OpGenerator> mPendingActions;
    Runnable[] mTmpActions;
    boolean mExecutingActions;

    ArrayList<Fragment> mActive;
    ArrayList<Fragment> mAdded;
    ArrayList<Integer> mAvailIndices;
    ArrayList<BackStackRecord> mBackStack;
    ArrayList<Fragment> mCreatedMenus;

// Must be accessed while locked.
    ArrayList<BackStackRecord> mBackStackIndices;
    ArrayList<Integer> mAvailBackStackIndices;

    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
//...
}

可以看到,FragmentManagerImpl 中定義了 新增的、活躍的。以及回退棧的列表,這和 FragmentManager 的要求一致。

int mCurState = Fragment.INITIALIZING;
FragmentHostCallback mHost;
FragmentContainer mContainer;
Fragment mParent;

static Field sAnimationListenerField = null;

boolean mNeedMenuInvalidate;
boolean mStateSaved;
boolean mDestroyed;
String mNoTransactionsBecause;
boolean mHavePendingDeferredStart;

接著還有當前的狀態,當前 Fragment 的起始 mParent,以及 FragmentManager 的 mHost 和 mContainer。

FragmentContainer 就是一個介面,定義了關於佈局的兩個方法:

public abstract class FragmentContainer {
    @Nullable
    public abstract View onFindViewById(@IdRes int id);
    public abstract boolean onHasView();
}

FragmentHostCallback 就複雜一點了,它提供了 Fragment 需要的資訊,也定義了 Fragment 宿主應該做的操作:

public abstract class FragmentHostCallback<E> extends FragmentContainer {
    private final Activity mActivity;
    final Context mContext;
    private final Handler mHandler;
    final int mWindowAnimations;
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    //...
}

我們知道,一般來說 Fragment 的宿主就兩種:

  1. Activity
  2. Fragment

比如 FragmentActivity 的內部類 HostCallbacks 就實現了這個抽象類:

class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
    public HostCallbacks() {
        super(FragmentActivity.this /*fragmentActivity*/);
    }
    //...

    @Override
    public LayoutInflater onGetLayoutInflater() {
        return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
    }

    @Override
    public FragmentActivity onGetHost() {
        return FragmentActivity.this;
    }

    @Override
    public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
    }

    @Override
    public void onStartActivityFromFragment(
            Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
    }

    @Override
    public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
            @NonNull String[] permissions, int requestCode) {
        FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
                requestCode);
    }

    @Override
    public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
        return ActivityCompat.shouldShowRequestPermissionRationale(
                FragmentActivity.this, permission);
    }

    @Override
    public boolean onHasWindowAnimations() {
        return getWindow() != null;
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        FragmentActivity.this.onAttachFragment(fragment);
    }

    @Nullable
    @Override
    public View onFindViewById(int id) {
        return FragmentActivity.this.findViewById(id);
    }

    @Override
    public boolean onHasView() {
        final Window w = getWindow();
        return (w != null && w.peekDecorView() != null);
    }
}

我們再看看他對 FragmentManager 定義的關鍵方法是如何實現的。

@Override
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

beginTransaction() 返回一個新的 BackStackRecord ,我們後面介紹。

前面提到了,popBackStack() 是一個非同步操作,它是如何實現非同步的呢?

@Override
public void popBackStack() {
    enqueueAction(new PopBackStackState(null, -1, 0), false);
}
public void enqueueAction(OpGenerator 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<>();
        }
        mPendingActions.add(action);
        scheduleCommit();
    }
}
private void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

可以看到,呼叫到最後,是呼叫宿主中的 Handler 來傳送任務的,so easy 嘛。其他的非同步執行也是類似,就不贅述了。

後退棧相關方法:

ArrayList<BackStackRecord> mBackStack;
@Override
public int getBackStackEntryCount() {
    return mBackStack != null ? mBackStack.size() : 0;
}

@Override
public BackStackEntry getBackStackEntryAt(int index) {
    return mBackStack.get(index);
}

可以看到,開始事務和後退棧,返回/操作的都是 BackStackRecord,我們來了解了解它是何方神聖。

事務

BackStackRecord 繼承了 FragmentTransaction

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

先來看看 FragmentTransaction

FragmentTransaction

FragmentTransaction 定義了一系列對 Fragment 的操作方法:

//它會呼叫 add(int, Fragment, String),其中第一個引數傳的是 0
public abstract FragmentTransaction add(Fragment fragment, String tag);

//它會呼叫 add(int, Fragment, String),其中第三個引數是 null
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);

//新增一個 Fragment 給 Activity 的最終實現
//第一個參數列示 Fragment 要放置的佈局 id
//第二個參數列示要新增的 Fragment,【注意】一個 Fragment 只能新增一次
//第三個引數選填,可以給 Fragment 設定一個 tag,後續可以使用這個 tag 查詢它
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment,
        @Nullable String tag);

//呼叫 replace(int, Fragment, String),第三個引數傳的是 null
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);

//替換宿主中一個已經存在的 fragment
//這一個方法等價於先呼叫 remove(), 再呼叫 add()
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment,
        @Nullable String tag);

//移除一個已經存在的 fragment
//如果之前新增到宿主上,那它的佈局也會被移除
public abstract FragmentTransaction remove(Fragment fragment);

//隱藏一個已存的 fragment
//其實就是將新增到宿主上的佈局隱藏
public abstract FragmentTransaction hide(Fragment fragment);

//顯示前面隱藏的 fragment,這隻適用於之前新增到宿主上的 fragment
public abstract FragmentTransaction show(Fragment fragment);

//將指定的 fragment 將佈局上解除
//當呼叫這個方法時,fragment 的佈局已經銷燬了
public abstract FragmentTransaction detach(Fragment fragment);

//當前面解除一個 fragment 的佈局繫結後,呼叫這個方法可以重新繫結
//這將導致該 fragment 的佈局重建,然後新增、展示到介面上
public abstract FragmentTransaction attach(Fragment fragment);

對 fragment 的操作基本就這幾步,我們知道,要完成對 fragment 的操作,最後還需要提交一下:

mFragmentManager.beginTransaction()
        .replace(R.id.fl_child, getChildFragment())
//        .commit()
        .commitAllowingStateLoss();

事務的四種提交方式

事務最終的提交方法有四種:

  1. commit()
  2. commitAllowingStateLoss()
  3. commitNow()
  4. commitNowAllowingStateLoss()

它們之間的特點及區別如下:

public abstract int commit();

commit() 在主執行緒中非同步執行,其實也是 Handler 丟擲任務,等待主執行緒排程執行。

注意:
commit() 需要在宿主 Activity 儲存狀態之前呼叫,否則會報錯。
這是因為如果 Activity 出現異常需要恢復狀態,在儲存狀態之後的 commit() 將會丟失,這和呼叫的初衷不符,所以會報錯。

public abstract int commitAllowingStateLoss();

commitAllowingStateLoss() 也是非同步執行,但它的不同之處在於,允許在 Activity 儲存狀態之後呼叫,也就是說它遇到狀態丟失不會報錯。

因此我們一般在介面狀態出錯是可以接受的情況下使用它。

public abstract void commitNow();

commitNow() 是同步執行的,立即提交任務。

前面提到 FragmentManager.executePendingTransactions() 也可以實現立即提交事務。但我們一般建議使用 commitNow(), 因為另外那位是一下子執行所有待執行的任務,可能會把當前所有的事務都一下子執行了,這有可能有副作用。

此外,這個方法提交的事務可能不會被新增到 FragmentManger 的後退棧,因為你這樣直接提交,有可能影響其他非同步執行任務在棧中的順序。

commit() 一樣,commitNow() 也必須在 Activity 儲存狀態前呼叫,否則會拋異常。

public abstract void commitNowAllowingStateLoss();

同步執行的 commitAllowingStateLoss()

OK,瞭解了 FragmentTransaction 定義的操作,去看看我們真正關心的、 beginTransaction() 中返回的 BackStackRecord:

@Override
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

事務真正實現/回退棧 BackStackRecord

BackStackRecord 既是對 Fragment 進行操作的事務的真正實現,也是 FragmentManager 中的回退棧的實現:

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {...}

它的關鍵成員:


final FragmentManagerImpl mManager;

//Op 可選的狀態值
static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
static final int OP_DETACH = 6;
static final int OP_ATTACH = 7;

ArrayList<Op> mOps = new ArrayList<>();
static final class Op {
    int cmd;    //狀態
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;
}

int mIndex = -1;    //棧中最後一個元素的索引

可以看到 Op 就是新增了狀態和動畫資訊的 Fragment, mOps 就是棧中所有的 Fragment。

事務定義的方法它是如何實現的呢。

先看新增一個 Fragment 到佈局 add() 的實現

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

@Override
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) {
    final Class fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
            || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
        throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                + " must be a public static class to be  properly recreated from"
                + " instance state.");
    }

    //1.修改新增的 fragmentManager 為當前棧的 manager
    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.mTag = tag;
    }

    if (containerViewId != 0) {
        if (containerViewId == View.NO_ID) {
            throw new IllegalArgumentException("Can't add fragment "
                    + fragment + " with tag " + tag + " to container view with no id");
        }
        if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
            throw new IllegalStateException("Can't change container ID of fragment "
                    + fragment + ": was " + fragment.mFragmentId
                    + " now " + containerViewId);
        }
        //2.設定宿主 ID 為佈局 ID
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }

    //3.構造 Op
    Op op = new Op();
    op.cmd = opcmd;    //狀態
    op.fragment = fragment;
    //4.新增到陣列列表中
    addOp(op);
}
void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}

可以看到新增一個 Fragment 到佈局很簡單,概況一下就是:
修改 fragmentManager 和 ID,構造成 Op,設定狀態資訊,然後新增到列表裡。

新增完了看看替換 replace 的實現

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

@Override
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;
}

太可怕了,也是呼叫上面剛提到的 doAddOp(),不同之處在於第四個引數為 OP_REPLACE,看來之前小看了這個狀態值!

再看其他方法的實現就很簡單了,無非就是構造一個 Op,設定對應的狀態值。

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

    return this;
}

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

    return this;
}

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

    return this;
}

那這些狀態值的不同是什麼時候起作用的呢?

別忘了我們操作 Fragment 還有最後一步,提交。

看看這兩個是怎麼實現的:

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

@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}
int commitInternal(boolean allowStateLoss) {
    if (mCommitted) throw new IllegalStateException("commit already called");
    //...
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);    //更新 index 資訊
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);    //非同步任務入隊
    return mIndex;
}
public void enqueueAction(OpGenerator 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<>();
        }
        mPendingActions.add(action);
        scheduleCommit();    //傳送任務
    }
}
private void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

前面已經介紹過了,FragmentManager.enqueueAction() 最終是使用 Handler 實現的非同步執行。

現在的問題是執行的任務是啥?

答案就是 Handler 傳送的任務 mExecCommit:

Runnable mExecCommit = new Runnable() {
    @Override
    public void run() {
        execPendingActions();
    }
};
/**
 * Only call from main thread!
 * 更新 UI 嘛,肯定得在主執行緒
 */
public boolean execPendingActions() {
    ensureExecReady(true);

    boolean didSomething = false;
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);    //這裡是入口
        } finally {
            cleanupExec();
        }
        didSomething = true;
    }

    doPendingDeferredStart();

    return didSomething;
}
private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop) {
    if (records == null || records.isEmpty()) {
        return;
    }

    if (isRecordPop == null || records.size() != isRecordPop.size()) {
        throw new IllegalStateException("Internal error with the back stack records");
    }

    // Force start of any postponed transactions that interact with scheduled transactions:
    executePostponedTransaction(records, isRecordPop);

    final int numRecords = records.size();
    int startIndex = 0;
    for (int recordNum = 0; recordNum < numRecords; recordNum++) {
        final boolean canOptimize = records.get(recordNum).mAllowOptimization;
        if (!canOptimize) {
            // execute all previous transactions
            if (startIndex != recordNum) {
                //這裡將 Ops 過濾一遍
                executeOpsTogether(records, isRecordPop, startIndex, recordNum);
            }
            // execute all unoptimized pop operations together or one add operation
              //...
    }
    if (startIndex != numRecords) {
        executeOpsTogether(records, isRecordPop, startIndex, numRecords);
    }
}
private void executeOpsTogether(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
    boolean addToBackStack = false;
    if (mTmpAddedFragments == null) {
        mTmpAddedFragments = new ArrayList<>();
    } else {
        mTmpAddedFragments.clear();
    }
    if (mAdded != null) {
        mTmpAddedFragments.addAll(mAdded);
    }
    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        final BackStackRecord record = records.get(recordNum);
        final boolean isPop = isRecordPop.get(recordNum);
        if (!isPop) {
            record.expandReplaceOps(mTmpAddedFragments);    //修改狀態
        } else {
            record.trackAddedFragmentsInPop(mTmpAddedFragments);    
        }
        addToBackStack = addToBackStack || record.mAddToBackStack;
    }
    mTmpAddedFragments.clear();

    if (!allowOptimization) {
        FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
                false);
    }
    //真正處理的入口
    executeOps(records, isRecordPop, startIndex, endIndex);

    int postponeIndex = endIndex;
    if (allowOptimization) {
        ArraySet<Fragment> addedFragments = new ArraySet<>();
        addAddedFragments(addedFragments);
        postponeIndex = postponePostponableTransactions(records, isRecordPop,
                startIndex, endIndex, addedFragments);
        makeRemovedFragmentsInvisible(addedFragments);    //名字就能看出來作用
    }

    if (postponeIndex != startIndex && allowOptimization) {
        // need to run something now
        FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                postponeIndex, true);
        moveToState(mCurState, true);
    }
    //...
}
//修改 Ops 狀態,這一步還沒有真正處理狀態
expandReplaceOps(ArrayList<Fragment> added) {
    for (int opNum = 0; opNum < mOps.size(); opNum++) {
        final Op op = mOps.get(opNum);
        switch (op.cmd) {
            case OP_ADD:
            case OP_ATTACH:
                added.add(op.fragment);
                break;
            case OP_REMOVE:
            case OP_DETACH:
                added.remove(op.fragment);
                break;
            case OP_REPLACE: {     
                Fragment f = op.fragment;
                int containerId = f.mContainerId;
                boolean alreadyAdded = false;
                for (int i = added.size() - 1; i >= 0; i--) {
                    Fragment old = added.get(i);
                    if (old.mContainerId == containerId) {
                        if (old == f) {
                            alreadyAdded = true;
                        } else {
                            Op removeOp = new Op();
                            removeOp.cmd = OP_REMOVE;    //可以看到,替換也是透過刪除實現的
                            removeOp.fragment = old;
                            removeOp.enterAnim = op.enterAnim;
                            removeOp.popEnterAnim = op.popEnterAnim;
                            removeOp.exitAnim = op.exitAnim;
                            removeOp.popExitAnim = op.popExitAnim;
                            mOps.add(opNum, removeOp);
                            added.remove(old);
                            opNum++;
                        }
                    }
                }
                if (alreadyAdded) {
                    mOps.remove(opNum);
                    opNum--;
                } else {
                    op.cmd = OP_ADD;
                    added.add(f);
                }
            }
            break;
        }
    }
}
//設定將要被移除的 Fragment 為不可見的最終實現
private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) {
    final int numAdded = fragments.size();
    for (int i = 0; i < numAdded; i++) {
        final Fragment fragment = fragments.valueAt(i);
        if (!fragment.mAdded) {
            final View view = fragment.getView();    //獲取 Fragment 的佈局,設定狀態
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                fragment.getView().setVisibility(View.INVISIBLE);
            } else {        //高版本設定透明度
                fragment.mPostponedAlpha = view.getAlpha();
                view.setAlpha(0f);
            }
        }
    }
}

程式碼多了一點,但我們終於找到了最終的實現:Handler 非同步發到主線,排程執行後,聚合、修改 Ops 的狀態,然後遍歷、修改 Fragment 棧中的 View 的狀態。

真正處理的部分

前面主要是對 Fragment 的包裝類 Ops 進行一些狀態修改,真正根據 Ops 狀態進行操作在這個部分:

/**
 * Executes the operations contained within this transaction. The Fragment states will only
 * be modified if optimizations are not allowed.
 */
void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        f.setNextTransition(mTransition, mTransitionStyle);
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        if (!mAllowOptimization && op.cmd != OP_ADD) {
            mManager.moveFragmentToExpectedState(f);
        }
    }
    if (!mAllowOptimization) {
        // Added fragments are added at the end to comply with prior behavior.
        mManager.moveToState(mManager.mCurState, true);
    }
}

FragmentManager 對這些方法的實現也很簡單,修改 Fragment 的狀態值,比如 remove(Fragment):

public void removeFragment(Fragment fragment) {
    if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
    final boolean inactive = !fragment.isInBackStack();
    if (!fragment.mDetached || inactive) {
        if (mAdded != null) {
            mAdded.remove(fragment);
        }
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        fragment.mAdded = false;    //設定屬性值
        fragment.mRemoving = true;
    }
}

最終會呼叫 moveToState(),我們直接來看它的實現:

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    //還沒有新增的 Fragment 處於 onCreate() 狀態
    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;
    }
    //推遲啟動的設定為 stop
    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.getAnimatingAway() != 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.setAnimatingAway(null);
            //如果當前 Fragment 正有動畫,直接修改為最終狀態
            moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
        }
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                if (f.mSavedFragmentState != null) {
                    f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                    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();
                dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                f.mCalled = false;
                f.onAttach(mHost.getContext());    //呼叫 Fragment 生命週期方法
                if (!f.mCalled) {
                    throw new SuperNotCalledException("Fragment " + f
                            + " did not call through to super.onAttach()");
                }
                if (f.mParentFragment == null) {
                    mHost.onAttachFragment(f);
                } else {
                    f.mParentFragment.onAttachFragment(f);
                }
                dispatchOnFragmentAttached(f, mHost.getContext(), false);

                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState); //呼叫 Fragment 生命週期方法

                    dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                } else {
                    f.restoreChildFragmentState(f.mSavedFragmentState);
                    f.mState = Fragment.CREATED;
                }
                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(    //呼叫 Fragment 生命週期方法
                            f.mSavedFragmentState), null, f.mSavedFragmentState);
                    if (f.mView != null) {
                        f.mInnerView = f.mView;
                        if (Build.VERSION.SDK_INT >= 11) {
                            ViewCompat.setSaveFromParentEnabled(f.mView, false);
                        } else {
                            f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                        }
                        if (f.mHidden) f.mView.setVisibility(View.GONE);
                        f.onViewCreated(f.mView, f.mSavedFragmentState);    //呼叫 Fragment 生命週期方法
                        dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
                    } else {
                        f.mInnerView = null;
                    }
                }
            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) {
                            if (f.mContainerId == View.NO_ID) {
                                throwException(new IllegalArgumentException(
                                        "Cannot create fragment "
                                                + f
                                                + " for a container view with no id"));
                            }
                            container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                            if (container == null && !f.mRestored) {
                                String resName;
                                try {
                                    resName = f.getResources().getResourceName(f.mContainerId);
                                } catch (NotFoundException e) {
                                    resName = "unknown";
                                }
                                throwException(new IllegalArgumentException(
                                        "No view found for id 0x"
                                        + Integer.toHexString(f.mContainerId) + " ("
                                        + resName
                                        + ") for fragment " + f));
                            }
                        }
                        f.mContainer = container;
                        f.mView = f.performCreateView(f.getLayoutInflater( //呼叫 Fragment 生命週期方法
                                f.mSavedFragmentState), container, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mInnerView = f.mView;
                            if (Build.VERSION.SDK_INT >= 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }
                            if (container != null) {
                                container.addView(f.mView);       //將 Fragment 的佈局新增到父佈局中
                            }
                            if (f.mHidden) {
                                f.mView.setVisibility(View.GONE);
                            }
                            f.onViewCreated(f.mView, f.mSavedFragmentState);//呼叫 Fragment 生命週期方法
                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                    false);
                            // Only animate the view if it is visible. This is done after
                            // dispatchOnFragmentViewCreated in case visibility is changed
                            f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                                    && f.mContainer != null;
                        } else {
                            f.mInnerView = null;
                        }
                    }

                    f.performActivityCreated(f.mSavedFragmentState); //呼叫 Fragment 生命週期方法

                    dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                    if (f.mView != null) {
                        f.restoreViewState(f.mSavedFragmentState);
                    }
                    f.mSavedFragmentState = null;
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState > Fragment.ACTIVITY_CREATED) {
                    f.mState = Fragment.STOPPED;
                }
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                    dispatchOnFragmentStarted(f, false);
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    dispatchOnFragmentResumed(f, false);
                    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();
                    dispatchOnFragmentPaused(f, false);
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                    f.performStop();
                    dispatchOnFragmentStopped(f, false);
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            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();
                    dispatchOnFragmentViewDestroyed(f, false);
                    if (f.mView != null && f.mContainer != null) {
                        Animation anim = null;
                        if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                && f.mView.getVisibility() == View.VISIBLE
                                && f.mPostponedAlpha >= 0) {
                            anim = loadAnimation(f, transit, false,
                                    transitionStyle);
                        }
                        f.mPostponedAlpha = 0;
                        if (anim != null) {
                            final Fragment fragment = f;
                            f.setAnimatingAway(f.mView);
                            f.setStateAfterAnimating(newState);
                            final View viewToAnimate = f.mView;
                            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                    viewToAnimate, anim) {
                                @Override
                                public void onAnimationEnd(Animation animation) {
                                    super.onAnimationEnd(animation);
                                    if (fragment.getAnimatingAway() != null) {
                                        fragment.setAnimatingAway(null);
                                        moveToState(fragment, fragment.getStateAfterAnimating(),
                                                0, 0, false);
                                    }
                                }
                            });
                            f.mView.startAnimation(anim);
                        }
                        f.mContainer.removeView(f.mView);
                    }
                    f.mContainer = null;
                    f.mView = null;
                    f.mInnerView = null;
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    if (mDestroyed) {
                        if (f.getAnimatingAway() != 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.
                            View v = f.getAnimatingAway();
                            f.setAnimatingAway(null);
                            v.clearAnimation();
                        }
                    }
                    if (f.getAnimatingAway() != 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.setStateAfterAnimating(newState);
                        newState = Fragment.CREATED;
                    } else {
                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                        if (!f.mRetaining) {
                            f.performDestroy();
                            dispatchOnFragmentDestroyed(f, false);
                        } else {
                            f.mState = Fragment.INITIALIZING;
                        }

                        f.performDetach();
                        dispatchOnFragmentDetached(f, false);
                        if (!keepActive) {
                            if (!f.mRetaining) {
                                makeInactive(f);
                            } else {
                                f.mHost = null;
                                f.mParentFragment = null;
                                f.mFragmentManager = null;
                            }
                        }
                    }
                }
        }
    }

    if (f.mState != newState) {
        Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
                + "expected state " + newState + " found " + f.mState);
        f.mState = newState;
    }
}

程式碼很長,但做的事情很簡單:

  1. 根據狀態呼叫對應的生命週期方法
  2. 如果是新建立的,就把佈局新增到 ViewGroup 中

Fragment 是什麼

Fragment 是什麼,從官網、別人部落格上看到的都是他人之言,我們還是得去看原始碼才能得到答案。

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {...}

可以看到,Fragment 沒有繼承任何類,只是實現了這兩個介面,第二個不太重要,第一個是在記憶體不足時可以收到回撥。

沒有什麼特別資訊,我們還是去看看它的主要成員。

Fragment 的主要成員


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.

//當前 Fragment 的狀態值
int mState = INITIALIZING;
//...
// True if the fragment is in the list of added fragments.
boolean mAdded;

// If set this fragment is being removed from its activity.
boolean mRemoving;

// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;

// Set to true when the view has actually been inflated in its layout.
boolean mInLayout;

// True if this fragment has been restored from previously saved state.
boolean mRestored;

// Number of active back stack entries this fragment is in.
int mBackStackNesting;

// Set to true when the app has requested that this fragment be hidden
// from the user.
boolean mHidden;

// Set to true when the app has requested that this fragment be deactivated.
boolean mDetached;

// If set this fragment would like its instance retained across
// configuration changes.
boolean mRetainInstance;

// If set this fragment is being retained across the current config change.
boolean mRetaining;

// If set this fragment has menu items to contribute.
boolean mHasMenu;

// Set to true to allow the fragment's menu to be shown.
boolean mMenuVisible = true;

// Used to verify that subclasses call through to super class.
boolean mCalled;

一堆標誌位和狀態值。然後就是關鍵的成員了:

// The fragment manager we are associated with.  Set as soon as the
// fragment is used in a transaction; cleared after it has been removed
// from all transactions.
FragmentManagerImpl mFragmentManager;

//Fragmemt 繫結的物件,一半就是 Activity 和 Fragment
FragmentHostCallback mHost;
//管理子 Fragment
FragmentManagerImpl mChildFragmentManager;

// For use when restoring fragment state and descendant fragments are retained.
// This state is set by FragmentState.instantiate and cleared in onCreate.
FragmentManagerNonConfig mChildNonConfig;
//如果這個 Fragment 繫結的是另一個 Fragment,就需要設定這個值
Fragment mParentFragment;
//容器 Fragment 的ID
int mFragmentId;
//容器 View 的ID
int mContainerId;

//父佈局
ViewGroup mContainer;

//當前 Fragment 的佈局
View mView;

//真正儲存狀態的內部佈局
View mInnerView;

看到這裡,結合前面的,我們就清晰了一個 Fragment 的建立、新增過程:

onCreateView() 中返回一個 佈局,然後在 FragmentManager 中拿到這個佈局,新增到要繫結容器(Activity/Fragment)的 ViewGroup 中,然後設定相應的狀態值。

生命週期方法

Fragment 的生命週期大家都清楚,官方提供了一張很清晰的圖:

這裡寫圖片描述

總共 11 個方法,這裡我們看一下各個方法的具體原始碼。

1. onAttach(Context)

@CallSuper
public void onAttach(Context context) {
    mCalled = true;
    final Activity hostActivity = mHost == null ? null : mHost.getActivity();
    if (hostActivity != null) {
        mCalled = false;
        onAttach(hostActivity);
    }
}

@Deprecated
@CallSuper
public void onAttach(Activity activity) {
    mCalled = true;
}

onAttach() 是一個 Fragment 和它的 Context 關聯時第一個呼叫的方法,這裡我們可以獲得對應的 Context 或者 Activity,可以看到這裡拿到的 Activity 是 mHost.getActivity(),後面我們介紹 FragmentManager 時介紹這個方法。

2. onCreate(Bundle)

public void onCreate(@Nullable Bundle savedInstanceState) {
    mCalled = true;
    restoreChildFragmentState(savedInstanceState);
    if (mChildFragmentManager != null
            && !mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) {
        mChildFragmentManager.dispatchCreate();
    }
}
void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        Parcelable p = savedInstanceState.getParcelable(
                FragmentActivity.FRAGMENTS_TAG);
        if (p != null) {
            if (mChildFragmentManager == null) {
                instantiateChildFragmentManager();
            }
            mChildFragmentManager.restoreAllState(p, mChildNonConfig);
            mChildNonConfig = null;
            mChildFragmentManager.dispatchCreate();
        }
    }
}

onCreate()onAttach() 後呼叫,用於做一些初始化操作。

需要注意的是,Fragment 的 onCreate() 呼叫時關聯的 Activity 可能還沒建立好,所以這裡不要有依賴外部 Activity 佈局的操作。如果有依賴 Activity 的操作,可以放在 onActivityCreate() 中。

從上面的程式碼還可以看到,如果是從舊狀態中恢復,會執行子 Fragment 狀態的恢復,此外還在 onCreate() 中呼叫了子 Fragment 管理者的建立。

3. onCreateView(LayoutInflater, ViewGroup, Bundle)

@Nullable
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
    return null;
}

onCreate() 後就會執行 onCreatView(),這個方法返回一個 View,預設返回為 null。

當我們需要在 Fragment 中顯示佈局時,需要重寫這個方法,返回要顯示的佈局。

後面佈局銷燬時就會呼叫 onDestroyView()

3.1. onViewCreated

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
}

onViewCreate() 不是生命週期中的方法,但是卻很有用。

它會在 onCreateView() 返回後立即執行,引數中的 view 就是之前建立的 View,因此我們可以在 onViewCreate() 中進行佈局的初始化,比如這樣:

@Override
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
    if (view == null) {
        return;
    }
    mTextView = (TextView) view.findViewById(R.id.tv_content);
    mBtnSwitchChild = (Button) view.findViewById(R.id.btn_switch_child);

    Bundle arguments = getArguments();
    if (arguments != null && mTextView != null && !TextUtils.isEmpty(arguments.getString(KEY_TITLE))) {
        mTextView.setText(arguments.getString(KEY_TITLE));
    }
    mBtnSwitchChild.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            //...
    });
}

4. onActivityCreated(Bundle)

@CallSuper
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    mCalled = true;
}

onActivityCreated() 會在 Fragment 關聯的 Activity 建立好、Fragment 的佈局結構初始化完成後呼叫。

可以在這個方法裡做些和佈局、狀態恢復有關的操作。

4.1 onViewStateRestored(Bundle)

@CallSuper
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    mCalled = true;
}

onViewStateRestored() 方法會在 onActivityCreated() 結束後呼叫,用於一個 Fragment 在從舊的狀態恢復時,獲取狀態 saveInstanceState 恢復狀態,比如恢復一個 check box 的狀態。

經過這四步,Fragment 建立完成,同步於 Activity 的建立過程。

5. onStart()

@CallSuper
public void onStart() {
    mCalled = true;

    if (!mLoadersStarted) {
        mLoadersStarted = true;
        if (!mCheckedForLoaderManager) {
            mCheckedForLoaderManager = true;
            mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
        }
        if (mLoaderManager != null) {
            mLoaderManager.doStart();
        }
    }
}

onStart() 當 Fragment 可見時呼叫,同步於 Activity 的 onStart()

6. onResume()

@CallSuper
public void onResume() {
    mCalled = true;
}

onResume() 當 Fragment 可見並且可以與使用者互動時呼叫。

它和 Activity 的 onResume() 同步。

7. onPause()

@CallSuper
public void onPause() {
    mCalled = true;
}

onPause() 當 Fragment 不再可見時呼叫。

也和 Activity 的 onPause() 同步。

8. onStop()

@CallSuper
public void onStop() {
    mCalled = true;
}

onStop() 當 Fragment 不再啟動時呼叫,和 Activity.onStop() 一致。

9. onDestroyView()

@CallSuper
public void onDestroyView() {
    mCalled = true;
}

onCreateView() 返回的佈局(不論是不是 null)從 Fragment 中解除繫結時呼叫 onDestroyView()

下次 Fragment 展示時,會重新建立佈局。

10. onDestroy()

@CallSuper
public void onDestroy() {
    mCalled = true;
    //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
    //        + " mLoaderManager=" + mLoaderManager);
    if (!mCheckedForLoaderManager) {
        mCheckedForLoaderManager = true;
        mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
    }
    if (mLoaderManager != null) {
        mLoaderManager.doDestroy();
    }
}

當 Fragment 不再使用時會呼叫 onDestroy(),它是一個 Fragment 生命週期的倒數第二步。

可以看到這裡,呼叫了 mLoaderManager.doDestroy(),後面介紹它。

11. onDetach()

@CallSuper
public void onDetach() {
    mCalled = true;
}

Fragment 生命週期的最後一個方法,當 Fragment 不再和一個 Activity 繫結時呼叫。

Fragment 的 onDestroyView(), onDestroy(), onDetach() 三個對應 Activity 的 onDestroyed() 方法。

總結

OK,看完這篇文章,相信對開頭提出的問題你已經有了答案,這裡再總結一下。

Fragment、FragmentManager、FragmentTransaction 關係

  • Fragment
    • 其實是對 View 的封裝,它持有 view, containerView, fragmentManager, childFragmentManager 等資訊
  • FragmentManager
    • 是一個抽象類,它定義了對一個 Activity/Fragment 中 新增進來的 Fragment 列表Fragment 回退棧的操作、管理方法
    • 還定義了獲取事務物件的方法
    • 具體實現在 FragmentImpl 中
  • FragmentTransaction
    • 定義了對 Fragment 新增、替換、隱藏等操作,還有四種提交方法
    • 具體實現是在 BackStackRecord 中

Fragment 如何實現佈局的新增替換

透過獲得當前 Activity/Fragment 的 FragmentManager/ChildFragmentManager,進而拿到事務的實現類 BackStackRecord,它將目標 Fragment 構造成 Ops(包裝Fragment 和狀態資訊),然後提交給 FragmentManager 處理。

如果是非同步提交,就透過 Handler 傳送 Runnable 任務,FragmentManager 拿到任務後,先處理 Ops 狀態,然後呼叫 moveToState() 方法根據狀態呼叫 Fragment 對應的生命週期方法,從而達到 Fragment 的新增、佈局的替換隱藏等。

下面這張圖從下往上看就是一個 Fragment 建立經歷的方法:

巢狀 Fragment 的原理

也比較簡單,Fragment 內部有一個 childFragmentManager,透過它管理子 Fragment。

在新增子 Fragment 時,把子 Fragment 的佈局 add 到父 Fragment 即可。

相關文章