找了一圈,发现网上讲碎片的文章都是...,所以还是自己找源码吧
事务的实现类找了一下,发现是BackStackRecord,它里面存放的是你每一次事务操作的所有元数据(碎片,操作方式(add,remove...))。他放在了Op类里。用ArrayList类来保存。但是不要以为它就是你碎片的栈了。碎片栈存放在FragmentManagerImpl类里。每当BackStackRecord的Op集合改变以后,FragmentManagerImpl就会监听到这个情况,从而对真正的碎片栈进行操作。我们这里想探寻的是detach方法,所以只讲这部分的源码,其他东西可以自己看,很好找,代码也很好理解。
当你transaction.detach就是执行这里的代码了(代码在BackStackRecord类里)
@Override public FragmentTransaction detach(Fragment fragment) { Op op = new Op(); op.cmd = OP_DETACH; op.fragment = fragment; addOp(op); return this; }
仅仅是元数据集合,记录了每一次操作。这里的detach仅仅记录了操作,但是并没有真正地去执行操作。
所以你得看看执行操作的地方
void executeOps() { final int numOps = mOps.size(); for (int opNum = 0; opNum < numOps; opNum++) { final Op op = mOps.get(opNum); final Fragment f = op.fragment; f.setNextTransition(mTransition, mTransitionStyle); switch (op.cmd) { case OP_ADD: f.setNextAnim(op.enterAnim); mManager.addFragment(f, false); break; case OP_REMOVE: f.setNextAnim(op.exitAnim); mManager.removeFragment(f); break; case OP_HIDE: f.setNextAnim(op.exitAnim); mManager.hideFragment(f); break; case OP_SHOW: f.setNextAnim(op.enterAnim); mManager.showFragment(f); break; case OP_DETACH: f.setNextAnim(op.exitAnim); mManager.detachFragment(f); break; case OP_ATTACH: f.setNextAnim(op.enterAnim); mManager.attachFragment(f); break;
这里调用的就是FragmentManagerImpl类里的方法,所以我们得知了,其实FragmentManagerImpl类里才是真正存放了碎片栈,和对碎片栈进行的操作方法。
我们看一下detach方法。
public void detachFragment(Fragment fragment) { if (DEBUG) Log.v(TAG, "detach: " + fragment); if (!fragment.mDetached) { fragment.mDetached = true; if (fragment.mAdded) { // We are not already in back stack, so need to remove the fragment. if (mAdded != null) { if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); mAdded.remove(fragment); } if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; } } }
你只需要关注一句,remove。mAdd就是被添加到栈(也就是ViewGroup如FrameLayout中)的碎片的集合。有人可能会问,这和remove有啥区别?
看一下remove
public void removeFragment(Fragment fragment) { if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); final boolean inactive = !fragment.isInBackStack(); if (!fragment.mDetached || inactive) { if (mAdded != null) { mAdded.remove(fragment); } if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; fragment.mRemoving = true; } }
也是remove,多了一句mRemoving = true。有个锤子用?
所以可以考虑看一下addFragment方法。
public void addFragment(Fragment fragment, boolean moveToStateNow) { if (mAdded == null) { mAdded = new ArrayList<Fragment>(); } if (DEBUG) Log.v(TAG, "add: " + fragment); makeActive(fragment); if (!fragment.mDetached) { if (mAdded.contains(fragment)) { throw new IllegalStateException("Fragment already added: " + fragment); } mAdded.add(fragment); fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mView == null) { fragment.mHiddenChanged = false; } if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { moveToState(fragment); } } }根本看不出所以然嘛!但是你想想之前的executeOps方法。你应该领悟到了,FragmentManagerImpl类顾名思义就是碎片的管理者(的实现类)啊!
不过别急着去看BackStackRecord的add方法,先看看attach方法
@Override public FragmentTransaction attach(Fragment fragment) { Op op = new Op(); op.cmd = OP_ATTACH; op.fragment = fragment; addOp(op); return this; }看看,依然只是状态的改变,所以得知了,detach是把碎片从mAdd中移除了,其实remove也是从mAdd中移除了,可是不同的地方在BackStackRecord里。当你执行add的时候,你会传入新的碎片,传入要插入的ViewGroup,然后进行Op类状态的重新绑定。而detach,attach并不需要这样,他们换过来换过去也只是更改了几个变量而已。
而detach掉的碎片虽然从mAdd集合中移除了,但是他的状态都没有变。
看看add方法,验证猜测
@Override public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { doAddOp(containerViewId, fragment, tag, OP_ADD); return this; } private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { final Class fragmentClass = fragment.getClass(); final int modifiers = fragmentClass.getModifiers(); if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers) || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) { throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName() + " must be a public static class to be properly recreated from" + " instance state."); } 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 (containerViewId == View.NO_ID) { throw new IllegalArgumentException("Can't add fragment " + fragment + " with tag " + tag + " to container view with no id"); } 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); }
得出结论:remove和detach的都是从mAdd中移除,(可以这么认为)但是remove掉的碎片失去了Op类的信息,detach掉的碎片,依然保有。所以detach可以被认为是一个轻量级的移除(通俗点就是detach仅仅是和activity解绑,remove不仅解绑了,还从活动中移除了,甚至执行到了onDestory)。
最后一个疑问:为什么要有这个BackStackRecord呢?
其实,你所commit的事务并不是立刻提交的!而是经过九曲十八弯,最终抵达消息队列,再在子线程里解决这一个个操作。详细可以看文章:commit和commitNow的区别:http://blog.csdn.net/qq_36523667/article/details/78656078
(这两个类的源码值得一看,又简单,又实用,回报很高。此外看源码可以提升自己的逻辑,判空逻辑就是一点)