【Android源碼研讀】FragmentManager與FragmentTransaction

FragmentManager本身是一個抽象類,真正實現的是FragmentManager.java中的FragmentManagerImpl。

1. Add Fragment操作

在FragmentManagerImpl中,有一組add/remove/replace/attach...Fragment的操作,但通過FragmentManager的操作,是無法直接調用到的,例如下面。

public void addFragment(Fragment fragment, boolean moveToStateNow) {n if (mAdded == null) {n mAdded = new ArrayList<Fragment>();n }n if (DEBUG) Log.v(TAG, "add: " + fragment);n makeActive(fragment);n if (!fragment.mDetached) {n if (mAdded.contains(fragment)) {n throw new IllegalStateException("Fragment already added: " + fragment);n }n mAdded.add(fragment);n fragment.mAdded = true;n fragment.mRemoving = false;n if (fragment.mHasMenu && fragment.mMenuVisible) {n mNeedMenuInvalidate = true;n }n if (moveToStateNow) {n moveToState(fragment);n }n }n }n

為了搞清楚如何管理,轉向FragmentTransaction。

我們知道,向FragmentManager中添加fragment,都是通過fragmentTransaction;但FragmentTransaction只是一個抽象類,真正實現是BackStackRecord。

2 BackStackRecord

final class BackStackRecord extends FragmentTransaction implementsn FragmentManager.BackStackEntry, Runnablen

BackStackRecord不僅是FragmentTransaction的具體實現,更是一個Runnable。後面可以看到,真正執行添加、替換等操作的,均是在其run()方法中執行,由FragmentManager加到主線程的消息隊列中執行。

此外,包含一個雙向鏈表,用來串聯操作。此即為了支持FragmentTransaction的鏈式調用。

public BackStackRecord(FragmentManagerImpl manager) {n mManager = manager;n}nnn...n// 指向雙向鏈表頭尾的指針nOp mHead;nOp mTail;n

鏈表節點類型:

static final class Op {nnn // 雙向鏈表,用於向後、向前遍歷,對應commit()和popBackStack() 2種操作。n Op next;n Op prev;n // cmd存儲了是什麼類型的command。n // static final int OP_NULL = 0;n // static final int OP_ADD = 1;n // static final int OP_REPLACE = 2;n // static final int OP_REMOVE = 3;n // static final int OP_HIDE = 4;n // static final int OP_SHOW = 5;n // static final int OP_DETACH = 6;n // static final int OP_ATTACH = 7;n int cmd;n // 存儲了待操作的fragmentn Fragment fragment;n // 定義了動畫資源n int enterAnim;n int exitAnim;n int popEnterAnim;n int popExitAnim;n // 這個後面會看到n ArrayList<Fragment> removed;n }n

2.1 add, attach, detach, show, hide, remove, replace

所有的FragmentTransition的add, attach, detach, hide, remove, replace, show均是在增加這個Op。

add -> doAddOp(), 對應類型OP_ADDnreplace -> doAddOp(), 對應類型OP_REPLACEnattach -> addOp(), 對應類型OP_ATTACHndetach -> addOp(), 對應類型OP_DETACHnshow -> addOp(), 對應類型OP_SHOWnhide -> addOp(), 對應類型OP_HIDEnremove -> addOp(), 對應類型OP_REMOVEn

public FragmentTransaction add(Fragment fragment, String tag) {n doAddOp(0, fragment, tag, OP_ADD);n return this;n }npublic FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {n if (containerViewId == 0) {n throw new IllegalArgumentException("Must use non-zero containerViewId");n }nnn doAddOp(containerViewId, fragment, tag, OP_REPLACE);n return this;n }npublic FragmentTransaction remove(Fragment fragment) {n Op op = new Op();n op.cmd = OP_REMOVE;n op.fragment = fragment;n addOp(op);nnn return this;n }n ...n

2.2 doAddOp()

添加操作比較特殊,這個會留作以後分析。也是調用addOp();

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {n fragment.mFragmentManager = mManager;nnn if (tag != null) {n if (fragment.mTag != null && !tag.equals(fragment.mTag)) {n throw new IllegalStateException("Cant change tag of fragment "n + fragment + ": was " + fragment.mTagn + " now " + tag);n }n fragment.mTag = tag;n }nnn if (containerViewId != 0) {n if (containerViewId == View.NO_ID) {n throw new IllegalArgumentException("Cant add fragment "n + fragment + " with tag " + tag + " to container view with no id");n }n if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {n throw new IllegalStateException("Cant change container ID of fragment "n + fragment + ": was " + fragment.mFragmentIdn + " now " + containerViewId);n }n fragment.mContainerId = fragment.mFragmentId = containerViewId;n }nnn Op op = new Op();n op.cmd = opcmd;n op.fragment = fragment;n addOp(op);n }n

2.3 commit

調用鏈:

commit() -> commitInternal(false) -> mManager.enqueueAction(this, allowStateLoss);n

對於mManager的enqueueAction(this, allowStateLoss);

1. mManager中維護了一個所有待執行runnable的列表,該方法首先懶創建該列表,其次把this(BackStackRecord)作為Runnable加到這個mPenddingActions中。所有的操作其實都是放在BackStackRecord的run方法中。

2. 執行mExeCommit

public void enqueueAction(Runnable action, boolean allowStateLoss) {n if (!allowStateLoss) {n checkStateLoss();n }n synchronized (this) {n if (mDestroyed || mHost == null) {n throw new IllegalStateException("Activity has been destroyed");n }n if (mPendingActions == null) {n mPendingActions = new ArrayList<Runnable>();n }n mPendingActions.add(action);n // 這個size()==1,基本能保證這個Runnable被立即被加入到消息隊列中去。如果在mExecCommit執行過程中,有新的Runnable被加入,則可由下面的exePendingActions()的循環處理n if (mPendingActions.size() == 1) {n mHost.getHandler().removeCallbacks(mExecCommit);n mHost.getHandler().post(mExecCommit);n }n }n }n

mExecCommit:

Runnable,僅運行一個方法:execPendingActions();

execPendingActions()

while (true) {n int numActions;n // 鎖加在這裡,只拿所有penddingActionn synchronized (this) {n if (mPendingActions == null || mPendingActions.size() == 0) {n break;n }n numActions = mPendingActions.size();n if (mTmpActions == null || mTmpActions.length < numActions) {n mTmpActions = new Runnable[numActions];n }n mPendingActions.toArray(mTmpActions);n mPendingActions.clear();n mHost.getHandler().removeCallbacks(mExecCommit);n }n mExecutingActions = true;n for (int i=0; i<numActions; i++) {n mTmpActions[i].run();n mTmpActions[i] = null;n }n mExecutingActions = false;n didSomething = true;n }n

這裡的鎖,與enqueueAction中的鎖,既保證了新加入的runnable被即時執行,也保證了可以持續加入之間的平衡,大家細細體會。

這裡也可以看出,commit()並不是立即執行。

最後,回到了BackStackRecord的run方法中。

2.4 BackStackRecord.run()

public void run() {n if (FragmentManagerImpl.DEBUG) {n Log.v(TAG, "Run: " + this);n }nnn if (mAddToBackStack) {n if (mIndex < 0) {n throw new IllegalStateException("addToBackStack() called after commit()");n }n }nnn bumpBackStackNesting(1);nnn // 以下是對與其動畫的處理。n if (mManager.mCurState >= Fragment.CREATED) {n // 建立2個array,存儲對於每一個containerId的,最後進入的fragment,以及首個離開的fragment。出入動畫,只對應最後進入和首個離開這個container的fragmentn SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();n SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();n // 通過這個方法,遍歷整個鏈表,對每個containerId對應的ViewGroup,計算出首個進入lastInFragment,及firstOutFragment,並分別存入SparseArray。n calculateFragments(firstOutFragments, lastInFragments);n beginTransition(firstOutFragments, lastInFragments, false);n }nnn // 下面遍歷整個鏈表n Op op = mHead;n while (op != null) {n switch (op.cmd) {n // 遇到添加,直接添加,加入到mManager的mAdd List中。n case OP_ADD: {n Fragment f = op.fragment;n f.mNextAnim = op.enterAnim;n // 這裡才調用了文章開頭所說的FragmentManagerImpl的addFragment方法n mManager.addFragment(f, false);n }n break;n // 替換,要把替換的fragment(被刪除的),存入該backStackRecord的mRemoved中,為隨後的回退做準備。n case OP_REPLACE: {n Fragment f = op.fragment;n int containerId = f.mContainerId;n if (mManager.mAdded != null) {n for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {n Fragment old = mManager.mAdded.get(i);n if (FragmentManagerImpl.DEBUG) {n Log.v(TAG,n "OP_REPLACE: adding=" + f + " old=" + old);n }n if (old.mContainerId == containerId) {n if (old == f) {n op.fragment = f = null;n } else {n if (op.removed == null) {n op.removed = new ArrayList<Fragment>();n }n //將被替換的,加入到removed list中。n op.removed.add(old);n old.mNextAnim = op.exitAnim;n if (mAddToBackStack) {n old.mBackStackNesting += 1;n if (FragmentManagerImpl.DEBUG) {n Log.v(TAG, "Bump nesting of "n + old + " to " + old.mBackStackNesting);n }n }n mManager.removeFragment(old, mTransition, mTransitionStyle);n }n }n }n }n if (f != null) {n f.mNextAnim = op.enterAnim;n mManager.addFragment(f, false);n }n }n break;n case OP_REMOVE: {n Fragment f = op.fragment;n f.mNextAnim = op.exitAnim;n mManager.removeFragment(f, mTransition, mTransitionStyle);n }n break;n case OP_HIDE: {n Fragment f = op.fragment;n f.mNextAnim = op.exitAnim;n mManager.hideFragment(f, mTransition, mTransitionStyle);n }n break;n case OP_SHOW: {n Fragment f = op.fragment;n f.mNextAnim = op.enterAnim;n mManager.showFragment(f, mTransition, mTransitionStyle);n }n break;n case OP_DETACH: {n Fragment f = op.fragment;n f.mNextAnim = op.exitAnim;n mManager.detachFragment(f, mTransition, mTransitionStyle);n }n break;n case OP_ATTACH: {n Fragment f = op.fragment;n f.mNextAnim = op.enterAnim;n mManager.attachFragment(f, mTransition, mTransitionStyle);n }n break;n default: {n throw new IllegalArgumentException("Unknown cmd: " + op.cmd);n }n }nnn op = op.next;n }nnn mManager.moveToState(mManager.mCurState, mTransition,n mTransitionStyle, true);nnn // 如果要可回退,則加入到mManager的n if (mAddToBackStack) {n mManager.addBackStackState(this);n }n }n

這裡設計巧妙之處在於,replace的操作,將所有被替換的fragment加入到了該backStackRecord的removed列表中,為後面回退做準備。

順便看下FragmentManager中關於mBackStack的定義,以及addBackStackState()函數的代碼。

...n ArrayList<BackStackRecord> mBackStack;nnn...nnn void addBackStackState(BackStackRecord state) {n if (mBackStack == null) {n mBackStack = new ArrayList<BackStackRecord>();n }n mBackStack.add(state);n reportBackStackChanged();n }n

2.5 FragmentManager.popBackStack()

同樣是調用enqueueAction,不一定立即執行,而僅在恰當時期執行。

enqueueAction(new Runnable() {n @Override public void run() {n popBackStackState(mHost.getHandler(), null, -1, 0);n }n }, false);n

其調用了一個函數popBackStackState(), 內部比較複雜,但最終都調用了BackStackRecord的popFromBackStack方法。

這個方法思路與run()類似,先處理動畫,然後執行操作,只不過,操作都是反著執行了。

Op op = mTail;n while (op != null) {n switch (op.cmd) {n //添加變刪除n case OP_ADD: {n Fragment f = op.fragment;n f.mNextAnim = op.popExitAnim;n mManager.removeFragment(f,n FragmentManagerImpl.reverseTransit(mTransition),n mTransitionStyle);n }n break;n // 替換,先把被刪除的從mRemove中加回來。再刪除替換的n case OP_REPLACE: {n Fragment f = op.fragment;n if (f != null) {n f.mNextAnim = op.popExitAnim;n mManager.removeFragment(f,n FragmentManagerImpl.reverseTransit(mTransition),n mTransitionStyle);n }n if (op.removed != null) {n for (int i = 0; i < op.removed.size(); i++) {n Fragment old = op.removed.get(i);n old.mNextAnim = op.popEnterAnim;n mManager.addFragment(old, false);n }n }n }n break;n case OP_REMOVE: {n Fragment f = op.fragment;n f.mNextAnim = op.popEnterAnim;n mManager.addFragment(f, false);n }n break;n case OP_HIDE: {n Fragment f = op.fragment;n f.mNextAnim = op.popEnterAnim;n mManager.showFragment(f,n FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);n }n break;n case OP_SHOW: {n Fragment f = op.fragment;n f.mNextAnim = op.popExitAnim;n mManager.hideFragment(f,n FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);n }n break;n case OP_DETACH: {n Fragment f = op.fragment;n f.mNextAnim = op.popEnterAnim;n mManager.attachFragment(f,n FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);n }n break;n case OP_ATTACH: {n Fragment f = op.fragment;n f.mNextAnim = op.popExitAnim;n mManager.detachFragment(f,n FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);n }n break;n default: {n throw new IllegalArgumentException("Unknown cmd: " + op.cmd);n }n }nnn op = op.prev;n }n

addFragment

最後,搞清楚了調用鏈,我們再回過來分析addFragment這個方法。

public void addFragment(Fragment fragment, boolean moveToStateNow) {n if (mAdded == null) {n mAdded = new ArrayList<Fragment>();n }n if (DEBUG) Log.v(TAG, "add: " + fragment);n n // makeActive,是將fragment加入到mActive列表中,n makeActive(fragment);n if (!fragment.mDetached) {n if (mAdded.contains(fragment)) {n throw new IllegalStateException("Fragment already added: " + fragment);n }n mAdded.add(fragment);n fragment.mAdded = true;n fragment.mRemoving = false;n if (fragment.mHasMenu && fragment.mMenuVisible) {n mNeedMenuInvalidate = true;n }n n // 重要的是這個moveToState方法,負責進行fragment的狀態轉移。n if (moveToStateNow) {n moveToState(fragment);n }n }n }n

moveToState()

忽略一些細節來看代碼

switch (f.mState) {n // 初始化, 即創建fragment的第一個狀態n case Fragment.INITIALIZING:n ...n n // 將該fragment的host activiy 和 parent framgment更新,並更新其fragmentManagern f.mHost = mHost;n f.mParentFragment = mParent;n f.mFragmentManager = mParent != nulln ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();n n ...n // 調用onAttachn f.onAttach(mHost.getContext());n ...n n if (f.mParentFragment == null) {n mHost.onAttachFragment(f);n } else {n f.mParentFragment.onAttachFragment(f);n }nn if (!f.mRetaining) {n f.performCreate(f.mSavedFragmentState);n } else {n ...n }n ...n if (f.mFromLayout) {n // For fragments that are part of the content viewn // layout, we need to instantiate the view immediatelyn // and the inflater will take care of adding it.n n // fragment的performCreateView會調用fragment的onCreateView方法,返回View並將其保存在f的mView對象中。n f.mView = f.performCreateView(f.getLayoutInflater(n f.mSavedFragmentState), null, f.mSavedFragmentState);n if (f.mView != null) {n if (f.mHidden) f.mView.setVisibility(View.GONE);n f.onViewCreated(f.mView, f.mSavedFragmentState);n }n }n n // INITIALIZING完了就是CREATED狀態n case Fragment.CREATED:n if (newState > Fragment.CREATED) {n ...n // 從該fragmentManager的mContainer中獲取containerId的ViewGroup。n container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);n n ...n // 加入ViewGroup n container.addView(f.mView);n }n if (f.mHidden) f.mView.setVisibility(View.GONE);n f.onViewCreated(f.mView, f.mSavedFragmentState);n }n }nn f.performActivityCreated(f.mSavedFragmentState);n if (f.mView != null) {n f.restoreViewState(f.mSavedFragmentState);n }n f.mSavedFragmentState = null;n }n ...n

推薦閱讀:

Windows 界面開發的歷程
有關互聯網界的「借鑒」表達一點小小看法
從零開始的Android新項目1 - 架構搭建篇
#每天一個小目標#Unity技術分享(十)
我想開發一個APP,現在有React Native 和 API Cloud 兩個框架,我該如何選擇?

TAG:Android开发 | 移动开发 |