Fragment的解析(一)

原创
2014/10/16 10:42
阅读数 2.1K

Fragment 的生成方式有两种,一种是写在layout文件里的, 如:

<?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>

另一种是在运行时动态加载:

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


先看第二种是如何实现的。

首先明确几个关键的类,FragmentManager的继承类(实现类)是FragmentManagerImp,  FragmentTransaction的实现类是BackStackRecord。

FragmentManagerImp中 beginTransaction方法返回了BackStackRecord实例:

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


接下来我们来看看这个 事务 做了什么:

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


BackStackRecord不单继承了  FragmentTransaction ,同时实现了BackStackEntry 和Runnable 接口;

实现BackStackEntry 是为了让Fragmentmanager管理, 实现Runnable 是为了把BackStackRecord对象作为消息post到UI线程的消息队列中。backStackRecord是一个事务,该事务中可以执行一个或多个操作(add、remove等), 这些操作的实际执行都是在UI主线程中进行的。这些操作保存在一个双向链表中, 链表的头和尾分别是 mHead 和 mTail

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;


下面看这行代码做了什么:

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> 队列。






展开阅读全文
打赏
0
6 收藏
分享
加载中
更多评论
打赏
0 评论
6 收藏
0
分享
返回顶部
顶部