Fragment的解析(一)

Fragment 的生成方式有兩種,一種是寫在layout文件裏的, 如:java

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment
        android:id="@+id/titles"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="play.apilearn.HistoryActivity$TitlesFragment" />
   
</FrameLayout>

另外一種是在運行時動態加載:android

FragmentManager fm = getFragmentManager();
FragmentManager.enableDebugLogging(true);
int count = fm.getBackStackEntryCount();
Log.d(LOG_TAG," backstack size:" + count);
//fm.getBackStackEntryAt(0).getName();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null));
ft.addToBackStack(null);
ft.commit();


先看第二種是如何實現的。api

首先明確幾個關鍵的類,FragmentManager的繼承類(實現類)是FragmentManagerImp,  FragmentTransaction的實現類是BackStackRecord。安全

FragmentManagerImp中 beginTransaction方法返回了BackStackRecord實例:ide

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


接下來咱們來看看這個 事務 作了什麼:oop

FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null));
ft.addToBackStack(null);
ft.commit();


BackStackRecord不單繼承了  FragmentTransaction ,同時實現了BackStackEntry 和Runnable 接口;post

實現BackStackEntry 是爲了讓Fragmentmanager管理, 實現Runnable 是爲了把BackStackRecord對象做爲消息post到UI線程的消息隊列中。backStackRecord是一個事務,該事務中能夠執行一個或多個操做(add、remove等), 這些操做的實際執行都是在UI主線程中進行的。這些操做保存在一個雙向鏈表中, 鏈表的頭和尾分別是 mHead 和 mTailthis

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable {

    final FragmentManagerImpl mManager;
   
    // operation types
    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;
    
    static final class Op {
        Op next;
        Op prev;
        int cmd;
        //該操做的目標fragment
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        //對於 OP_REPLACE 操做,removed表示該操做中移除的fragment,它們和目標fragment共享一個containerId
        ArrayList<Fragment> removed;
    }
 
  ...
 
  Op mHead;
  Op mTail;
  
  ...
  
  boolean mAddToBackStack;
  boolean mAllowAddToBackStack = true;
  String mName;
  boolean mCommitted;
  int mIndex = -1;


下面看這行代碼作了什麼:spa

ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null));

定位到BackStackRecord的 replace 方法:線程

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 方法,注意操做類型 opcmd 的值是 OP_REPLACE,該值被記錄在Op對象中:

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);
    }

addOp又作了什麼呢? 注意BackStackRecord是一個事務,能夠包含有序的多個操做,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++;
    }


接下來看addToBackStack( String name ): 

ft.addToBackStack(null);

// BackStackRecord.java
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;
    }

能夠看到該方法只是 把 mAddToBackStack 標識置位true, 該標識在 FragmentManager執行BackStackRecord中記錄的操做時會用到。


最後是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(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;
    }


commit 調用了內部方法 commitInternal(..) , 在 commitInternal方法返回以前,調用了FragmentManagerImp的enqueAction, 這個action對象就是當前的BackStackRecord對象(用this表示),它是一個事務, 前面說過, BackStackRecord實現了 Runnable 接口,做爲一個action添加到FragmentManager的 pendingActions隊列中。 fragmentManager會向UI主線程消息隊列裏post一個執行消息(mExecCommit)。當消息被主線程取出執行(comsume)的時候,會執行pendingActions隊列裏的每個action的run方法,因此接下來應該看 BackStackRecord 的run()方法裏到底作了什麼,不難猜想,固然是以前記錄在 Op中的一系列(也多是一個)操做。

//FragmentManager.java

 Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    
    
    ....

/**
     * 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 || 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);
            }
        }
    }
    
    ...
    
    /**
     * Only call from main thread!
     */
    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");
        }
        
        ...

        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);
            }
            
            mExecutingActions = true;
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run(); // 執行BackStackRecord 對象的run 方法
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }
        
        
        ...
    }



另外這裏用到了 mAddToBackStack標識, 若爲TRUE, fragmentManager會給當前事務分配一個 index, 注意是在commit ()分配的, 若是你在commit後又調用了 addToBackStack(String name ) 方法,那麼mAddToBackStack 標識會被置位true, 可是卻沒有分配到index (默認 -1) 。當消息被執行時 (在事務的run方法中), mIndex 和 mAddToBackStack 同時被檢查,若是 mAddToBackStack 爲true 而 mIndex 小於 0, 

事務會拋出 IllegalStateException 。這是實現事務的一種安全檢查。

從 run()  方法中能夠看出這一邏輯:

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;
        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;
                    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);
                            if (f == null || old.mContainerId == f.mContainerId) {
                                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);
                                }
                            }
                        }
                    }
                    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;
        }

        mManager.moveToState(mManager.mCurState, mTransition,
                mTransitionStyle, true);

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

最後檢查了 mAddToBackStack 標識,mBackStack 是在activity 收到 返回事件時去檢查的。

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

Ps: 解釋下 bumpBackStackNesting(int amt), 這個方法的意思是,若是當前事務要放入activity的回退棧,就在當前事務涉及的fragment的mBackStackNesting字段中記錄當前事務處於回退棧的第幾項(棧頂是 1),字段mBackStackNesting能夠用來判斷fragment是否在回退棧中。

//BackStackRecord.java
void bumpBackStackNesting(int amt) {
        if (!mAddToBackStack) {
            return;
        }
        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
                + " by " + amt);
        Op op = mHead;
        while (op != null) {
            if (op.fragment != null) {
                op.fragment.mBackStackNesting += amt;
                if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                        + op.fragment + " to " + op.fragment.mBackStackNesting);
            }
            if (op.removed != null) {
                for (int i=op.removed.size()-1; i>=0; i--) {
                    Fragment r = op.removed.get(i);
                    r.mBackStackNesting += amt;
                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                            + r + " to " + r.mBackStackNesting);
                }
            }
            op = op.next;
        }
    }

//Fragment.java
final boolean isInBackStack() {
        return mBackStackNesting > 0;
    }


回到 run方法, 能夠看到根據相應的操做命令(ADD,REMOVE, REPLACE, HIDE, ATTACH等)調用FragmentManager的相應方法。 並在最後根據當前事務的 mAddToBackStack標識決定是否把當前事務加入FragmentManager的 mBackStack<BackStackRecord> 隊列。

相關文章
相關標籤/搜索