Fragment-Transaction 源码分析
概述
这篇文章的简要分析了Activity中的Transaction和add,replace等操作以及backstack的工作原理。
分析transaction源码的原因是因为我在写一个测试代码的时候,发现replace并没有将之前所有添加到某个container id上的Fragment全部移除掉,觉得很奇怪。
查看官方API对replace的解释
Replace an existing fragment that was addedto a container. This is essentially the same as calling remove(Fragment) forall currently added fragments that were added with the same containerViewId andthen add(int,Fragment, String) with the same arguments given here.
怎么会和我测试的结果不一样,于是开始查看源代码。最后发现应该是一个Bug,然后将这个bug report在https://code.google.com/p/android/issues/detail?id=68856&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
看看是我理解得不透彻还是确实算个Bug,哈哈。
要测试的内容如下
首先我们以下面这段代码为例,来分析Fragment的add操作
点击一个Button的时候执行下面的操作
FragmentTransaction ft =getFragmentManager().beginTransaction();
ft.add(R.id.container,FragmentA.newInstance("1"));
ft.add(R.id.container,FragmentA.newInstance("2"));
ft.add(R.id.container,FragmentA.newInstance("3"));
ft.add(R.id.container,FragmentA.newInstance("4"));
ft.add(R.id.container,FragmentA.newInstance("5"));
ft.add(R.id.container,FragmentA.newInstance("6"));
ft.add(R.id.container,FragmentA.newInstance("7"));
ft.addToBackStack(null);
ft.commit();
接着点击另外一个按钮执行下面的操作:
FragmentTransaction ft =getFragmentManager().beginTransaction();
ft.replace(R.id.container,FragmentA.newInstance("replaced A"));
ft.addToBackStack(null);
ft.commit();
getFragmentManager
进入Activity的源码,我们发现Activity中有个FragmentManagerImpl,这个实例用来管理Fragment的添加,删除,以及保存当前的激活的Fragment,等。
Activity类
final FragmentManagerImpl mFragments = newFragmentManagerImpl();
public FragmentManager getFragmentManager() {
returnmFragments;
}
beginTransaction
当我们调用beginTransaction的时候,FragmentManager会为我们生成一个transaction,这个transaction其实是一个保存你即将进行的一些列操作的栈。比如你要add一个Fragment,接着又要replace一个Fragment,这两个操作都会被当做两个操作顺序的记录在这个transaction中。根据名字也可以看出来
(BackStackRecord,是一个实现了FragmentTransaction的类),
FragmentManagerImpl 类
ArrayList<BackStackRecord> mBackStackIndices;
@Override
public FragmentTransaction beginTransaction() {
return newBackStackRecord(this);
}
FragmentManager.add
接着我们调用了ft.add(R.id.cntainer, FragmentA.newInstance("1"));,这个操作将会向新生成的BackStackRecord对象中添加一个操作事件,即Op对象。Op对象相当于一个双向链表,记录了前一个操作和后一个操作。比如我们这次add了7个FragmentA,那么这7个操作会当成7个Op存放在这个新生成的BackStackRecord(就是一个Transaction)中。
public FragmentTransaction add(intcontainerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
returnthis;
}
private void doAddOp(int containerViewId, Fragmentfragment, 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 offragment "
+ fragment + ":was " + fragment.mFragmentId
+ " now " +containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
//关键代码:生成Op来记录这次操作。
Op op =new Op();
op.cmd= opcmd;
op.fragment = fragment;
addOp(op);
}
//关键代码:将Op添加到新生成的这个BackStackRecord中,会将这次trascation事件中的每个Op顺序的记录下来。
voidaddOp(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++;
}
static finalclass Op {
Opnext;
Opprev;
intcmd;
Fragment fragment;
intenterAnim;
intexitAnim;
intpopEnterAnim;
intpopExitAnim;
ArrayList<Fragment> removed;
}
紧接着,我们调用了ft.addToBackStack(null);
publicFragmentTransaction addToBackStack(String name) {
if(!mAllowAddToBackStack) {
throw new IllegalStateException(
"This FragmentTransaction is not allowed to be added to the backstack.");
}
//关键代码:将mAddToBackStack设置成true,这会在commit的时候判断,以便于将这个transaction添加到FragmentManager的Back stack栈中
mAddToBackStack = true;
mName =name;
returnthis;
}
FragmentManager.commit
最后,我们调用了ft.commit(),然后,FragmentManager就会将这次的所有Op放到主线程中去按顺序执行。BackStackRecord实现了run方法,所以在主线程执行的时候会调用run方法中的代码。
public int commit() {
returncommitInternal(false);
}
intcommitInternal(boolean allowStateLoss) {
if(mCommitted) throw new IllegalStateException("commit alreadycalled");
if(FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw,null);
}
mCommitted = true;
//关键代码:判断是否需要添加到Back stack
if(mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else{
mIndex = -1;
}
//关键代码:将这个transaction放入到FragmentManager的执行队列中去,当要开始执行这个transaction的时候,就会调用到BackStackRecord的run方法。
mManager.enqueueAction(this, allowStateLoss);
returnmIndex;
}
这里,我们看看FragmentManager的allocBackStackIndex方法。我们发现FragmentManger的back stack就是一个ArrayList,里面记录了一些列的transaction。
publicint allocBackStackIndex(BackStackRecord bse) {
synchronized (this) {
if (mAvailBackStackIndices == null|| mAvailBackStackIndices.size() <= 0) {
if (mBackStackIndices == null){
//关键代码:生成Back stack的list
mBackStackIndices = newArrayList<BackStackRecord>();
}
int index =mBackStackIndices.size();
if (DEBUG) Log.v(TAG,"Setting back stack index " + index + " to " + bse);
//关键代码:添加这次的transaction到Backstack的list
mBackStackIndices.add(bse);
return index;
} else {
int index =mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
if (DEBUG) Log.v(TAG,"Adding back stack index " + index + " with " + bse);
mBackStackIndices.set(index, bse);
return index;
}
}
}
Add,replace源码
继续刚才的情况,在commit之后,会把这次的transaction放到FragmentManager的执行队列中去,当开始执行这个Transaction的时候,会调用到这个BackStackRecord的run方法。这个方法中会顺序的调用这个BackStackRecord中的所有Op,我们刚才已经分析指导,每个add,remove等操作都当做Op存放在这个Transaction中,BackStackRecord就是一个Transaction的实例。在处理一个Op的时候,会根据这个Op的cmd属性来进行add,remove,replace等操作。
publicvoid run() {
if (FragmentManagerImpl.DEBUG)Log.v(TAG, "Run: " + this);
if (mAddToBackStack) {
if (mIndex < 0) {
throw newIllegalStateException("addToBackStack() called after commit()");
}
}
bumpBackStackNesting(1);
Op op = mHead;
//关键代码:顺序处理这个transaction中的所有op
while (op != null) {
//关键代码:根据op的cmd属性分别进行add,replace等操作
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) {
//关键代码:我们这篇文章的重点来了,为什么只移除掉了一部分的Fragment呢?在这个For循环中,本来目的是要找出所有添加到这个mContainerId上的所有的Fragment,将他们从FragmentManager管理的mAdded表(这个表记录了Add到这个Activity的Fragment)中移除。但是我们看到在for循环里,i在不断的增加,但是实际上当从mManager .mAdded移除掉一个Fragment的时候,这个i的位置已经不对了。比如最开始我们的mAdded里面有序号为1,2,3,4,5,6,7的Fragment,在i
==0的时候,移除掉了序号为1的Fragment,所以mAdded里面还有序号为2,3,4,5,6,7的Fragment。接着i==1的时候,移除序号为3的Fragment。依次类推,序号为2,4,6的Fragment就没有被移除掉,所以就出现了文章开头处的问题。
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 = newArrayList<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 newIllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
op = op.next;
}
mManager.moveToState(mManager.mCurState,mTransition,
mTransitionStyle, true);
//关键代码:将这个Transaction添加到backstack中去。
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
按back回退
按back首先调用的是Activity的onBackPressed
public void onBackPressed() {
//关键代码:让Activity的backstack处理,如果返回false,表示没有需要back的,所以当前activity就finish掉。
if(!mFragments.popBackStackImmediate()) {
finish();
}
}
接着会去到FragmentManager的popBackStackImmediate
public boolean popBackStackImmediate() {
checkStateLoss();
executePendingTransactions();
returnpopBackStackState(mActivity.mHandler, null, -1, 0);
}
booleanpopBackStackState(Handler handler, String name, int id, int flags) {
if(mBackStack == null) {
return false;
}
if(name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE)== 0) {
intlast = mBackStack.size()-1;
if(last < 0) {
return false;
}
finalBackStackRecord bss = mBackStack.remove(last);
//关键代码:从FragmentManager管理的backstack中取出一个transaction(就是我们刚才说的BackStackRecord),调用他的popFromBackStack来还原之前的状态。
bss.popFromBackStack(true);
reportBackStackChanged();
} else{
intindex = -1;
if(name != null || id >= 0) {
// If a name or ID is specified, look for that place in
// the stack.
index = mBackStack.size()-1;
while (index >= 0) {
BackStackRecord bss = mBackStack.get(index);
if (name != null && name.equals(bss.getName())) {
break;
}
if (id >= 0 && id == bss.mIndex) {
break;
}
index--;
}
if (index < 0) {
return false;
}
if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
index--;
// Consume all following entries that match.
while (index >= 0) {
BackStackRecord bss =mBackStack.get(index);
if ((name != null&& name.equals(bss.getName()))
|| (id >= 0&& id == bss.mIndex)) {
index--;
continue;
}
break;
}
}
}
if(index == mBackStack.size()-1) {
return false;
}
final ArrayList<BackStackRecord> states
= new ArrayList<BackStackRecord>();
for(int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
final int LAST = states.size()-1;
for(int i=0; i<=LAST; i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " +states.get(i));
states.get(i).popFromBackStack(i == LAST);
}
reportBackStackChanged();
}
returntrue;
}
紧接着我们看看BackStackRecord是怎么来处理的
publicvoid popFromBackStack(boolean doStateMove) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "popFromBackStack:" + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = newPrintWriter(logw);
dump(" ", null, pw, null);
}
bumpBackStackNesting(-1);
//关键代码:在这里又开始循环取出这个transaction中的Op(就是那些add,replace等操作)。然后做出一些跟刚才add,replace相反的操作.
Op op = mTail;
while (op != null) {
switch (op.cmd) {
//关键代码:如果之前这个Transaction是Add操作,那么我们就用FragmentManager来将这个Fragment移除掉。
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim =op.popExitAnim;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
} break;
//关键代码:如果之前是Replace操作,我们就将之前在Replace操作的时候remove掉得那些Fragment再次add进来
caseOP_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.popEnterAnim;
mManager.detachFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),mTransitionStyle);
} break;
default: {
throw newIllegalArgumentException("Unknown cmd: " + op.cmd);
}
}
op = op.prev;
}
if (doStateMove) {
mManager.moveToState(mManager.mCurState,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle,true);
}
if (mIndex >= 0) {
mManager.freeBackStackIndex(mIndex);
mIndex = -1;
}
}
Fragment-Transaction 源码分析的更多相关文章
- spring transaction源码分析--事务架构
1. 引言 事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事 ...
- redis源码分析之事务Transaction(下)
接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...
- redis源码分析之事务Transaction(上)
这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...
- documentsUI源码分析
documentsUI源码分析 本文基于Android 6.0的源码,来分析documentsUI模块. 原本基于7.1源码看了两天,但是Android 7.1与6.0中documentsUI模块差异 ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- jQuery 2.0.3 源码分析 Deferred概念
JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...
- Spring源码分析——资源访问利器Resource之实现类分析
今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...
- angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...
随机推荐
- python 代码编写规范
一 代码编排1 缩进.4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格.2 每行最大长度79,换行可以使用反斜杠,最好使用圆括号.换行点要在操作符的后边敲回车.3 类 ...
- Laravel核心解读--HTTP内核
Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而 ...
- 转载-- Qt Creator编译时make: arm-linux-g++: command not found 错误!
前提是已经配置好交叉编译器,但是qt creator找不到. 解决方法: 修改 /usr/local/Trolltech/QtEmbedded-4.7.0-arm/mkspecs/qws/linux- ...
- new期间的异常
new包含两步,调用operator new申请空间,以及调用构造函数. 如果第一步结束之后,第二步发生异常,需要归还第一步的空间. 编译器帮我们做了这件事情,并且会调用对应的delete. 另外 n ...
- Java NIO和IO的主要差别
我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景.以及它们怎样影响您的代码设计. Java NIO和IO的主要差别 下表总结了Java N ...
- Tensorflow MNIST 数据集測试代码入门
本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50614444 測试代码已上传至GitH ...
- LicManager系统对各license类型终端客户机器的监控
与catia软件相似.以下这些软件都是汽车project设计软件.对汽车工业的的研发设计有着不可替代的作用.但它们都有着不同于各自的优势与不足之处. 可是在LicManager许可监控系统下,它们都是 ...
- thinkphp5项目--企业单车网站(一)
thinkphp5项目--企业单车网站(一) 项目地址 fry404006308/BicycleEnterpriseWebsite: Bicycle Enterprise Websitehttps:/ ...
- 机器学习(三) Jupyter Notebook, numpy和matplotlib的详细使用 (下)
七.Numpy中的矩阵运算 八.Numpy中的聚合运算 九.Numpy中的arg运算 十.Numpy中的比较和Fancy Indexing 十一.Matplotlib数据可视化基础 十二.数据加载和简 ...
- msiexec
msiexec: runCmd = new String[]{ "msiexec", "/i", exeName, "/quiet", &q ...