1. 简介:
本文章基于Fragment管理源码简介,对BackStackRecord的相关细节技术实现做详细的解读。
2. 主要的操作方法
从源码可以看到BackStackRecord中对Fragment的操作方法都是对FragmentTransaction的实现,主要的方法包括:
add、replace、remove、hide、show、detach、attach、addToBackStack、commit/ commitAllowingStateLoss,这是开发过程中常用到的几个方法,当然FragmentTransaction还有好多其它的方法可以自己去看源码。
3. 操作Op对象的命令:
OP_NULL、OP_ADD、OP_REPLACE、OP_REMOVE、OP_HIDE、OP_SHOW、OP_DETACH、OP_ATTACH,对应的都是整型值,其实可以看出和上面的操作方法时一一对应的。
4. 操作Op对象
//Op对象是一个双向链表结构 static final class Op { Op next;//上一个操作 Op prev;//下一个操作 int cmd;//操作命令对应上面3中的命令 Fragment fragment;//操作的fragment int enterAnim;//进入动画 int exitAnim;//退出动画 int popEnterAnim;//pop入栈动画 int popExitAnim;//pop出栈动画 ArrayList<Fragment> removed;//这个对象用于replace时,保存当前contianerId上所有的被替换的fragment,这里缓存下来用于回退栈时用。 } |
5. run方法
在Fragment管理简介文章中说过,BackStackRecord实现了Runnable接口,并且在FragmentManager中会执行这个run方法,所以这个run方法是fragment操作的开始。
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);
if (mManager.mCurState >= Fragment.CREATED) { SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); calculateFragments(firstOutFragments, lastInFragments); beginTransition(firstOutFragments, lastInFragments, false); }
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; int containerId = f.mContainerId; if (mManager.mAdded != null) { for (int i = mManager.mAdded.size() - 1; i >= 0; i--) { 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<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); } } |
主要看一下while循环对op链表遍历并执行相关FragmentManager操作的地方,run方法中的操纵是正向操作(即按照BackStackRecord的操作顺序),可以看出和FragmentManager的对应关系:
OP_ADD:FM.addFragment
OP_REPLACE: FM.removeFragment(相同containerId的fragment);FM.addFragment(新添加的fragment)
OP_REMOVE:FM.removeFragment
OP_HIDE:FM.hideFragment
OP_SHOW:FM.showFragment
OP_DETACH:FM.detachFragment
OP_ATTACH:FM.attachFragment
6. popFromBackStack方法
上面的run方法是BackStackRecord的正向操作,popFromBackStack则是反向操作。当BackStackRecord被压栈,则pop时会对BackStackRecord的操作进行反向的操作,简单的说就是页面返回操作。
public TransitionState popFromBackStack(boolean doStateMove, TransitionState state, SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "popFromBackStack: " + this); LogWriter logw = new LogWriter(Log.VERBOSE, TAG); PrintWriter pw = new FastPrintWriter(logw, false, 1024); dump(" ", null, pw, null); pw.flush(); }
if (mManager.mCurState >= Fragment.CREATED) { if (state == null) { if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { state = beginTransition(firstOutFragments, lastInFragments, true); } } else if (!doStateMove) { setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); } }
bumpBackStackNesting(-1);
Op op = mTail; while (op != null) { switch (op.cmd) { case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = op.popExitAnim; mManager.removeFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; case OP_REPLACE: { Fragment f = op.fragment; if (f != null) { f.mNextAnim = op.popExitAnim; mManager.removeFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } if (op.removed != null) { for (int i = 0; i < op.removed.size(); i++) { Fragment old = op.removed.get(i); old.mNextAnim = op.popEnterAnim; mManager.addFragment(old, false); } } } break; case OP_REMOVE: { Fragment f = op.fragment; f.mNextAnim = op.popEnterAnim; mManager.addFragment(f, false); } break; case OP_HIDE: { Fragment f = op.fragment; f.mNextAnim = op.popEnterAnim; mManager.showFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; case OP_SHOW: { Fragment f = op.fragment; f.mNextAnim = op.popExitAnim; mManager.hideFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; case OP_DETACH: { Fragment f = op.fragment; f.mNextAnim = op.popEnterAnim; mManager.attachFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; case OP_ATTACH: { Fragment f = op.fragment; f.mNextAnim = op.popExitAnim; mManager.detachFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } }
op = op.prev; }
if (doStateMove) { mManager.moveToState(mManager.mCurState, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true); state = null; }
if (mIndex >= 0) { mManager.freeBackStackIndex(mIndex); mIndex = -1; } return state; } |
主要看一下while循环对op的反向操作,和FragmentManager对应的关系为:
OP_ADD:FM.removeFragment
OP_REPLACE: FM.removeFragment(当前OP的fragment);FM.addFragment(OP中revomed的fragments即之前删除的相同containerId的所有fragments)
OP_REMOVE:FM.addFragment
OP_HIDE:FM.showFragment
OP_SHOW:FM.hideFragment
OP_DETACH:FM.attachFragment
OP_ATTACH:FM.detachFragment
7. 计算FirstOutFragment和LastInFragment
在BackStackRecord中还有几个方法比较值得关注,他们时用于计算当前BackStackRecord中第一个出栈的Fragment以及最后一个添加的Fragment,即setFirstOut和setLastIn以及calculateFragments和calculateBackFragments。
calculateFragments和calculateBackFragments方法的实现和run与popFromBackStack实现有点类似,不同的时对应的操作命令调用的时setFirstOut和setLastIn,具体代码这里就不贴出来了,可以看源代码,这里列出对应的关系:
calculateFragments方法是正向的:
OP_ADD:setLastIn(统计最后一个加入的Fragment)
OP_REPLACE: setFirstOut(将之前添加在同一个containerId的fragment都添加到出栈列表中);setLastInOP_REMOVE:setFirstOut
OP_HIDE:setFirstOut
OP_SHOW:setLastIn
OP_DETACH:setFirstOut
OP_ATTACH:setLastIn
以上是正向的操作关系;
calculateBackFragments方法是方向的:
OP_ADD:setFirstOut
OP_REPLACE: setLastIn(将之前添加在同一个containerId的fragment都添加到入栈列表中);setFirstOut
OP_REMOVE:setLastIn
OP_HIDE:setLastIn
OP_SHOW:setFirstOut
OP_DETACH:setLastIn
OP_ATTACH:setFirstOut
以上是反向的操作关系。
一个containerId只关联一个firstOutFragment和一个lastInFragment对象。
那么这两个方法计算出FirstOutFragment和LastInFragment的意义何在呢?其实是用于fragment进入和出入时动画效果的。//todo 具体的动画机制还没弄清楚
8. BackStackState对象
在BackStackRecord类文件中还有一个BackStackState类,从名字上看是用于保存BackStackRecord状态的,具体的作用没有做太多深入,但其定义不是太复杂,从定义上看它的作用就是可以被序列化传输,然后可以通过其中的状态值实例化一个BackStackRecord对象,那么其实作用就是为BackStackRecord序列化传输用的。
- 总结
以上就是对BackStackRcord常用的细节点的解读,可以配合源码看一遍,好多人可能会在正向和反向对Op对象遍历的地方有点迷糊,这里需要弄清楚了然后在FragmentManager的moveToState方法时才能更好的理解为什么这么设计以及FragmentManager是如何切换Fragment状态的。