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 ...
随机推荐
- Python3基础笔记--常用模块
目录: 参考博客:Python 之路 Day5 - 常用模块学习 Py西游攻关之模块 一.time模块 二.random模块 三.os模块 四.sys模块 五.hashlib模块 六.logging模 ...
- 禁用cache
Z:\src\services\network\network_context.cc:http_cache_enabled
- 用SAXReader解析xml文档【转】
来源:http://blog.csdn.net/seayqrain/article/details/5024068 使用SAXReader需要导入dom4j-full.jar包. dom4j是一个Ja ...
- NodeJS学习笔记 进阶 (7)express+session实现简易身份认证(ok)
个人总结: 这篇文章讲解了express框架中如何使用session,主要用了express-session这个包.更多可以参考npm.js来看,读完这篇文章需要10分钟. 摘选自网络: 文档概览 本 ...
- 记intel杯比赛中各种bug与debug【其一】:安装intel caffe
因为intel杯创新软件比赛过程中,并没有任何记录.现在用一点时间把全过程重演一次用作记录. 学习 pytorch 一段时间后,intel比赛突然不让用 pytoch 了,于是打算转战intel ca ...
- linux基础入门(二)命令
原创作品,允许转载,转载时请务必声明作者信息和本声明. https://www.cnblogs.com/zhu520/p/10732334.html =[本人小白,有错指出.谢谢! 一:使用Secur ...
- 配置TL-WVR45G企业路由动态地址
1.打开浏览器,在地址栏输入http://192.168.1.1. 2.输入默认用户名密码:admin,登录. 3.[基本设置]->[lan设置]->[lan设置] ip地址改成:192 ...
- 个人学习源码的 HBase误区的总结 与 架构图
HDFS 的备份功能不是给 基于 HBase 等 基于HDFS 的项目做备份的. 如果 HBase 需要备份,那么久需要设置 备份(快照 )功能. HMaster . kafka 等无主结构并 ...
- MapReduce JOB 的输出与输出笔记。
提高 MapReduce 价值,自定义输入和输出. 比如跳过存储到 HDFS 中这个耗时的布置. 而只是从原始数据源接受数据,或者直接将数据发送给某些处理程序. 这些处理程序在 MapReduce 作 ...
- struts2文件过滤拦截器fileUpload以及各种文件类型
本文某些内容复制自:http://zhidao.baidu.com/link?url=F0Z-FqbZ83BOj_xXp_B8rgJDzUoeVSWGgXwPNP5fEdLU1nvBK7yO4vnX_ ...