Fragment的添加和删除及其源码分析
2016-06-01 19:40
471 查看
Android API中FragmentManager和FragmentTransaction这两个类中提供了大量的对Fragment进行操作的方法,这里做记录和整理。
findFragmentById():根据布局xml文件中声明的id查找对应的Fragment;
findFragmentByTag():根据动态添加Fragment的TAG值,查找对应的Fragment;
remove():删除Fragment,当该Fragment没有入栈,那么该Fragment的实例将会被删除,反之,该实例不会被删除;
replace():其实是先remove()然后调用add();
add()方法是FragmentTransaction中的方法,我们从这个类开始,进入FragmentTransaction后,发现该类是一个抽象类。查找该类的实现类:BackStackRecord,该类还实现了Runnable接口,我们来看看该类中的add()方法
发现add()的三个重载方法都调用doAppOp()这个方法,另外有一个地方我们需要注意,该方法的最后都加了一个标记参数:OP_ADD,我们来看看这个方法
我们可以看到该方法将参数进行了传递,最后调用了addOp()这个方法,上代码:
该方法中,只是进行了op对象的传递,和动画的参数传递,然后就没有下文了,难道到这就断了?并不是,在添加Fragment的时候,最后都会调用commit()方法,添加的Fragment才会真正的被添加上去,那我们来看看commit()方法的源码
其内部调用的是commitInternal(),继续看源码
该方法的最后,将该类加入了一个Action队列
该方法中将BackStackRecord加入了mPendingActions集合,接着来看这句代码
其也是一个Runnable,run方法中执行了execPendingActions(),我们再来看看这个方法:
该方法中mPendingActions与数组mTmpActions完成了置换,(不明白的可以去看下,toArray()方法的内部实现)最后for循环依次执行run方法。终于回归正轨,这里看下BackStackRecord中的run方法
内容比较庞大,但是还是决定贴出来,这里要说一下我们在之前提到的OP_ADD,这就是添加的标记,它的作用在该方法中提现出来了,除此以外,还有别的状态标记:replace,remove,hide,show,detach,attach这些;最后这些对应的标记下都调用了FragmentManagerImp中对应的方法。这里就不再贴源码了,感兴趣的朋友可以自行查看。
该方法最后两段代码稍微提一下:
这段代码中的moveToState方法体现了Fragment在不同生命周期状态下所进行的一些操作,大家可以看看源码
这段代码用来设置回退栈
remove()方法解析
结论上面已经贴出,这里只贴下证明结论的源码和简单解析
这句代码
replace()方法解析
上面源码直接可以看出,replace方法先调用了
这也是add方法与replace方法的区别
查找Fragment
其中FragmentManager中维护了一个队列,用于存放Fragment实例,我们添加的Fragment可以通过以下方法查找Fragment的查找
getFragmentManager()findFragmentById():根据布局xml文件中声明的id查找对应的Fragment;
findFragmentByTag():根据动态添加Fragment的TAG值,查找对应的Fragment;
getFragmentManager().findFragmentById(R.id.fragment_list); getFragmentManager().findFragmentById(TAG);
查找嵌套在Fragment内部的Fragment
getChildFragmentManager():当Fragment中嵌套着另外一个Fragment,注意这时候要使用getChildFragmentManager()获取对应的FragmentManager,否则会查找不到该Fragment导致空指针异常TestListFragment testListFragment = (TestListFragment) getChildFragmentManager().findFragmentById(R.id.fragment_list);
Fragment的添加,删除
添加Fragment
add():向Activity中添加Fragment;remove():删除Fragment,当该Fragment没有入栈,那么该Fragment的实例将会被删除,反之,该实例不会被删除;
replace():其实是先remove()然后调用add();
源码分析
add()方法解析add()方法是FragmentTransaction中的方法,我们从这个类开始,进入FragmentTransaction后,发现该类是一个抽象类。查找该类的实现类:BackStackRecord,该类还实现了Runnable接口,我们来看看该类中的add()方法
public FragmentTransaction add(Fragment fragment, String tag) { doAddOp(0, fragment, tag, OP_ADD); return this; } public FragmentTransaction add(int containerViewId, Fragment fragment) { doAddOp(containerViewId, fragment, null, OP_ADD); return this; } public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { doAddOp(containerViewId, fragment, tag, OP_ADD); return this; }
发现add()的三个重载方法都调用doAppOp()这个方法,另外有一个地方我们需要注意,该方法的最后都加了一个标记参数:OP_ADD,我们来看看这个方法
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()这个方法,上代码:
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++; }
该方法中,只是进行了op对象的传递,和动画的参数传递,然后就没有下文了,难道到这就断了?并不是,在添加Fragment的时候,最后都会调用commit()方法,添加的Fragment才会真正的被添加上去,那我们来看看commit()方法的源码
public int commit() { return commitInternal(false); }
其内部调用的是commitInternal(),继续看源码
int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }
该方法的最后,将该类加入了一个Action队列
mManager.enqueueAction(this, allowStateLoss);,继续看
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (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); } } }
该方法中将BackStackRecord加入了mPendingActions集合,接着来看这句代码
mActivity.mHandler.post(mExecCommit);,Handler执行mExecCommit,我们再来看看mExecCommit是什么
Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } };
其也是一个Runnable,run方法中执行了execPendingActions(),我们再来看看这个方法:
/** * 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"); } boolean didSomething = false; while (true) { int numActions; synchronized (this) { if (mPendingActions == null || mPendingActions.size() == 0) { return didSomething; } 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(); mTmpActions[i] = null; } mExecutingActions = false; didSomething = true; } }
该方法中mPendingActions与数组mTmpActions完成了置换,(不明白的可以去看下,toArray()方法的内部实现)最后for循环依次执行run方法。终于回归正轨,这里看下BackStackRecord中的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 (old.mContainerId == f.mContainerId) { 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); } } } 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); } }
内容比较庞大,但是还是决定贴出来,这里要说一下我们在之前提到的OP_ADD,这就是添加的标记,它的作用在该方法中提现出来了,除此以外,还有别的状态标记:replace,remove,hide,show,detach,attach这些;最后这些对应的标记下都调用了FragmentManagerImp中对应的方法。这里就不再贴源码了,感兴趣的朋友可以自行查看。
该方法最后两段代码稍微提一下:
mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true);
这段代码中的moveToState方法体现了Fragment在不同生命周期状态下所进行的一些操作,大家可以看看源码
if (mAddToBackStack) { mManager.addBackStackState(this); }
这段代码用来设置回退栈
remove()方法解析
结论上面已经贴出,这里只贴下证明结论的源码和简单解析
public void removeFragment(Fragment fragment, int transition, int transitionStyle) { if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); final boolean inactive = !fragment.isInBackStack(); if (!fragment.mDetached || inactive) { mAdded.remove(fragment); if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; fragment.mRemoving = true; moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, transition, transitionStyle); } }
这句代码
final boolean inactive = !fragment.isInBackStack();,获取当前fragment是否加入回退栈的状态,如果没加入将会进入方法体执行
mAdded.remove(fragment);,删除fragment实例
replace()方法解析
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 (old.mContainerId == f.mContainerId) { 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); } } } f.mNextAnim = op.enterAnim; mManager.addFragment(f, false); } break;
上面源码直接可以看出,replace方法先调用了
mManager.removeFragment(old, mTransition, mTransitionStyle);,最后调用了
mManager.addFragment(f, false);,这也验证了我们之前的结论。
这也是add方法与replace方法的区别
相关文章推荐
- cmd replace 文件替换使用说明
- ASP中让Replace替换不区分大小写的方法
- SQL中函数 replace 的参数1的数据类型ntext无效的解决方法
- Sql Server中REPLACE函数的使用
- asp提示无效使用 Null: Replace
- js继承 Base类的源码解析
- js replace 与replaceall实例用法详解
- javascript add event remove event
- 详解jQuery中的empty、remove和detach
- Jquery replace 字符替换实现代码
- 详解JS正则replace的使用方法
- js中replace的用法总结
- mysql数据库replace、regexp的用法
- javascript replace()方法的简单分析
- MySQL Replace INTO的使用
- MySQL中REPLACE INTO和INSERT INTO的区别分析
- mySQL中replace的用法
- JavaScript中使用replace结合正则实现replaceAll的效果
- MySQL replace函数替换字符串语句的用法
- 避免MySQL替换逻辑SQL的坑爹操作 推荐