- 1. Fragment基本用法
為了管理Activity中的fragments,需要調用Activity中的getFragmentManager()方法。因為FragmentManager的API是在Android 3.0,也即API level 11開始引入的,所以對於之前的版本,需要使用support library v4中的FragmentActivity,並且使用getSupportFragmentManager()方法。
用FragmentManager可以做的工作有:
得到Activity中存在的fragment:
使用findFragmentById()或findFragmentByTag()方法。
將fragment彈出back stack:
popBackStack():
將back stack中最后一次的fragment轉換彈出。如果沒有可以出棧的東西,返回false。
這個函數是異步的:它將彈出棧的請求加入隊列,但是這個動作直到應用回到事件循環才會執行。
為back stack加上監聽器:
addOnBackStackChangedListener()
使用Fragment時,可以執行一些動作,比如增加、移除、替換等。所有這些改變構成一個集合,這個集合被叫做一個transaction。
可以調用FragmentTransaction中的方法來處理這個transaction.
以這樣得到FragmentTransaction類的實例:
- FragmentManager fragmentManager = getFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每個transaction是一組同時執行的變化的集合。用add(), remove(), replace()方法,把所有需要的變化加進去,然后調用commit()方法,將這些變化應用。在commit()方法之前,你可以調用addToBackStack(),把這個transaction加入back stack中去,這個back stack是由activity管理的,當用戶按返回鍵時,就會回到上一個fragment的狀態。下面的代碼非常典型,用一個新的fragment取代之前的fragment,並且將之前的狀態存儲在back stack中。
- // Create new fragment and transaction
- Fragment newFragment = new ExampleFragment();
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- // Replace whatever is in the fragment_container view with this fragment,
- // and add the transaction to the back stack
- transaction.replace(R.id.fragment_container, newFragment);
- transaction.addToBackStack(null);
- // Commit the transaction
- transaction.commit();
調用commit()方法並不能立即執行transaction中包含的改變動作,commit()方法把transaction加入activity的UI線程隊列中。下面我們對上述代碼中出現的函數進行分析,以此來逐步學習Fragment的管理機制。
getSupportFragmentManager():
- public FragmentManager getSupportFragmentManager() {
- return mFragments;
- }
beginTransaction():
該函數在FragmentManagerIMpl中的源碼如下:- public FragmentTransaction beginTransaction() {
- return new BackStackRecord(this);
- }
BackStackRecord的聲明如下:- final class BackStackRecord extends FragmentTransaction implements
- FragmentManager.BackStackEntry, Runnable {...}
接下來執行transaction.replace(), 查看BackStackRecord,調用過程源碼如下:- 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;
- }
- 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.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.mContainerId = fragment.mFragmentId = containerViewId;
- }
- Op op = new Op();
- op.cmd = opcmd;
- op.fragment = fragment;
- addOp(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;
每個字段的意思可直接通過英文名稱獲知。Op()類是BackStackRecord中聲明的結構體,本質上是一個雙向鏈表的Node。addOp()如下:- void addOp(Op op) {
- if (mHead == null) {
- mHead = mTail = op;
- } else {
- op.prev = mTail;
- mTail.next = op;
- mTail = op;
- }
- op.enterAnim = mEnterAnim;
- op.exitAnim = mExitAnim;
- op.popEnterAnim = mPopEnterAnim;
- op.popExitAnim = mPopExitAnim;
- mNumOp++;
- }
transaction.addToBackStack(null)設置了mAddToBackStack為true,源碼如下:
- 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;
- }
最后調用transaction.commit()來執行transaction。commit()的調用過程代碼如下:- public int commit() {
- return commitInternal(false);
- }
- int commitInternal(boolean allowStateLoss) {
- if (mCommitted) throw new IllegalStateException("commit already called");
- if (FragmentManagerImpl.DEBUG) {
- Log.v(TAG, "Commit: " + this);
- LogWriter logw = new LogWriter(TAG);
- PrintWriter pw = new PrintWriter(logw);
- dump(" ", null, pw, null);
- }
- mCommitted = true;
- if (mAddToBackStack) {
- mIndex = mManager.allocBackStackIndex(this);
- } else {
- mIndex = -1;
- }
- mManager.enqueueAction(this, allowStateLoss);
- return mIndex;
- }
- public int allocBackStackIndex(BackStackRecord bse) {
- synchronized (this) {
- if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
- if (mBackStackIndices == null) {
- mBackStackIndices = new ArrayList<BackStackRecord>();
- }
- int index = mBackStackIndices.size();
- if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
- mBackStackIndices.add(bse);
- return index;
- } else {
- int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
- if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
- mBackStackIndices.set(index, bse);
- return index;
- }
- }
- }
讓我們回到commit()中,該函數最后執行mManager.enqueAction(),源碼如下:- public void enqueueAction(Runnable action, boolean allowStateLoss) {
- if (!allowStateLoss) {
- checkStateLoss();
- }
- synchronized (this) {
- if (mDestroyed || mActivity == null) {
- throw new IllegalStateException("Activity has been destroyed");
- }
- if (mPendingActions == null) {
- mPendingActions = new ArrayList<Runnable>();
- }
- mPendingActions.add(action);
- if (mPendingActions.size() == 1) {
- mActivity.mHandler.removeCallbacks(mExecCommit);
- mActivity.mHandler.post(mExecCommit);
- }
- }
- }
- 該函數首先進行狀態監測,查看該Fagment所在的Activity的生命周期是否處於Saving Activity之前,因為Activity保存狀態往往是由用戶離開那個Activity所造成的,在此之后執行commit會丟失一些狀態信息。針對這種情況,可以使用commitAllowingStateLoss().最后將BackStackRecord加入到執行隊列中。當第一次往執行
- 隊列中添加消息時,首先會從消息隊列中所有callback屬性為mExecCommit的消息刪除,然后重新將mExecCommit添加到消息隊列。mExecCommit的定義如下:
- Runnable mExecCommit = new Runnable() {
- @Override
- public void run() {
- execPendingActions();
- }
- };
- public boolean execPendingActions() {
- if (mExecutingActions) {
- throw new IllegalStateException("Recursive entry to executePendingTransactions");
- }
- if (Looper.myLooper() != mActivity.mHandler.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();
- mActivity.mHandler.removeCallbacks(mExecCommit);
- }
- //一次性執行完數組中所有的Action
- 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;
- }
- 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);
- Op op = mHead;
- //遍歷op,根據cmd的類型對Fragment和FragmentManager進行相應的設置
- while (op != null) {
- switch (op.cmd) {
- case OP_ADD: {
- Fragment f = op.fragment;
- f.mNextAnim = op.enterAnim;
- //將Fragment添加到FragmentManager中,其源碼顯示是將Fragment添加到FragmentManager中的mActive數組中,並將Fragment添加到了數組mAdded中。
- mManager.addFragment(f, false);
- } break;
- case OP_REPLACE: {
- Fragment f = op.fragment;
- if (mManager.mAdded != null) {
- //遍歷已經添加的Fragment,
- 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);
- //如果發現兩個mContainerId一樣,則進行特殊處理
- if (f == null || old.mContainerId == f.mContainerId) {
- if (old == f) {
- //兩個Fragment一樣,則置空,保留old中的Fragment
- op.fragment = f = null;
- } else {
- // 將old fragment加入到 op.removed數組中,保留op中的Fragment
- if (op.removed == null) {
- op.removed = new ArrayList<Fragment>();
- }
- op.removed.add(old);
- old.mNextAnim = op.exitAnim;
- if (mAddToBackStack) {
- //設置old Fragment在BackStack中的Number
- old.mBackStackNesting += 1;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + old + " to " + old.mBackStackNesting);
- }
- //對old Fragment設置相應的狀態屬性,如mAdded、mRemoving, 從FragmentManager中移除oldFrgment的相關屬性
- mManager.removeFragment(old, mTransition, mTransitionStyle);
- }
- }
- }
- }
- //將Fragment添加到FragmentManager中
- if (f != null) {
- f.mNextAnim = op.enterAnim;
- mManager.addFragment(f, false);
- }
- } break;
- case OP_REMOVE:
- ......
- }
- op = op.next;
- }
- //設置Fragment的當前狀態,然后根據當前狀態來回調Fragment的生命周期中的相關函數。此函數控制了Fragment的生命周期和Fragment的繪制,想要徹底理解Fragment的生命周期的同學可以認真研究此函數。
- mManager.moveToState(mManager.mCurState, mTransition,
- mTransitionStyle, true);
- //將BackStackRecord加入到BackStack中,並回調onBackStackChanged
- if (mAddToBackStack) {
- mManager.addBackStackState(this);
- }
- }
- void addBackStackState(BackStackRecord state) {
- if (mBackStack == null) {
- mBackStack = new ArrayList<BackStackRecord>();
- }
- mBackStack.add(state);
- //回調onBackStackChanged
- reportBackStackChanged();
- }
小結:
FragmentTransaction中的Op鏈用來保存add、remove、replace等action,在FragmentTransaction的run執行時,Op鏈會被變量以調整每個節點的內容。
FragmentManager使用一個BackStack來管理FragmentTransaction;使用mAdded數組來添加被add的Fragment,Fragment的創建、顯示等行為都受FragmentManager的控制。
FragmentManager中的moveToState()是一個非常重要的函數,在FragmentTransaction run的時候被調用。下次我們將深入這個函數。