作者:Yyao | 来源:互联网 | 2024-11-02 19:57
在探讨Fragment的使用时,FragmentTransaction是不可或缺的一部分。作为管理Fragment操作的核心类,FragmentTransaction提供了诸如显示、隐藏、添加和移除等方法,这些方法在实际开发中被广泛使用。本文将深入解析FragmentTransaction的源码实现机制,帮助开发者更好地理解和优化Fragment的管理。通过分析其内部工作原理,读者可以掌握如何高效地进行Fragment的动态管理和性能优化。
谈到fragment的使用,肯定绕不过FragmentTransaction事务,对fragment的操作必定用到它,其提供show,hide,add,remove,replace等常用的fragment操作,最后commit操作,这么强大的管理类,它内部是如何实现的呢?为什么可以连续调用多个api,最后一次要commit操作?
1、创建FragmentTransaction对象:
FragmentTransaction ft = mFragmentManager.beginTransaction();
源码实现:
@Override public FragmentTransaction beginTransaction () {return new BackStackRecord(this );}
可以看到new 出一个新的 BackStackRecord对象,这个BackStackRecord就是api的真正实现者。
我以remove fragment这个操作看下内部实现:
@Override public FragmentTransaction remove (Fragment fragment) {Op op = new Op();op.cmd = OP_REMOVE;op.fragment = fragment;addOp(op);return this ;}
这个Op是什么东东呢?,又把op加入到addOp中,我们不妨先看下这个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++;}
这段代码还是比较容易理解的,其实remove操作,就是新建一个 链表结点,结点中保存了当前的操作,保存了fragment的对象引用,所以 FragmentTransaction的内部主要实现就是通过链表操作的,每个链表结点保存了每一个api操作的信息,好了,我们看下commit的实现:
@Override public int commit () {return commitInternal(false );}
只能commit一次,否则抛出异常
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;}
看下 mManager.enqueueAction(this, allowStateLoss)的实现:
/*** 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 || mHost == null ) {throw new IllegalStateException("Activity has been destroyed" );}if (mPendingActions == null ) {mPendingActions = new ArrayList();}mPendingActions.add(action);if (mPendingActions.size() == 1 ) {mHost.getHandler().removeCallbacks(mExecCommit);mHost.getHandler().post(mExecCommit);}}}
代码解读: 1、先去检查 checkStateLoss 状态,正常commit操作,是肯定去检查的,如果是在onSaveInstanceState状态保存之后,再去commit操作,肯定会报错。
2、BackStackRecord 类实现了 Runnable,看其run方法。
public void run() {// 省略部分代码// 指向链表头指针Op op = mHead// 遍历链表结点while (op != null) {//每一个结点的cmd操作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 int containerId = f.mContainerId if (mManager.mAdded != null) {for (int i = mManager.mAdded .size () - 1 Fragment old = mManager.mAdded .get (i)if (FragmentManagerImpl.DEBUG ) {Log.v (TAG,"OP_REPLACE: adding=" + f + " old=" + old)}if (old.mContainerId == containerId) {if (old == f) {op.fragment = f = null} else {if (op.removed == null) {op.removed = new ArrayList()}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)// 是否调用了addBackStack(),如果加入,将当前的BackStackRecord加入到栈中if (mAddToBackStack) {mManager.addBackStackState (this)}}
这段代码解读:
1、每次commit之后,都会commit一个runnable任务,run()方法里面 对链表进行遍历操作,从头结点开始,依次访问每个结点,然后读取里面每一个结点的cmd,执行相应的方法。注意replace,是先remove,后add。 2、如果调用了addBackStack()方法,会将当前的任务对象加入到栈中。
void addBackStackState(BackStackRecord state) {if (mBackStack == null ) {mBackStack = new ArrayList();}mBackStack.add(state);reportBackStackChanged();}
可见我的这篇博客 从源码角度解释 fragment 坑(一)
我在想,FragmentTransaction 事务内部为什么要实现链表操作呢?
如果单纯的只是commit之前调用几个api,hide,show我认为可能没有必要,它的必要性,就是在退栈的时候,能够记住你之前的每个结点的行为,并且进行相应的反操作,比如我 先用事务,hide 1,remove 2,add 3,甚至更多,退栈的时候,我们是不是先执行,remove 3,往往3走到onDestroyview方法,2 调用 onCreateView,show 1出来,如果不用链表的操作的话,确实不太方便。