概述

这篇文章的简要分析了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 源码分析的更多相关文章

  1. spring transaction源码分析--事务架构

    1. 引言  事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事 ...

  2. redis源码分析之事务Transaction(下)

    接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...

  3. redis源码分析之事务Transaction(上)

    这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...

  4. documentsUI源码分析

    documentsUI源码分析 本文基于Android 6.0的源码,来分析documentsUI模块. 原本基于7.1源码看了两天,但是Android 7.1与6.0中documentsUI模块差异 ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. MyBatis源码分析-SQL语句执行的完整流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

  7. jQuery 2.0.3 源码分析 Deferred概念

    JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...

  8. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  9. angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了

    一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...

随机推荐

  1. python 代码编写规范

    一 代码编排1 缩进.4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格.2 每行最大长度79,换行可以使用反斜杠,最好使用圆括号.换行点要在操作符的后边敲回车.3 类 ...

  2. Laravel核心解读--HTTP内核

    Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而 ...

  3. 转载-- Qt Creator编译时make: arm-linux-g++: command not found 错误!

    前提是已经配置好交叉编译器,但是qt creator找不到. 解决方法: 修改 /usr/local/Trolltech/QtEmbedded-4.7.0-arm/mkspecs/qws/linux- ...

  4. new期间的异常

    new包含两步,调用operator new申请空间,以及调用构造函数. 如果第一步结束之后,第二步发生异常,需要归还第一步的空间. 编译器帮我们做了这件事情,并且会调用对应的delete. 另外 n ...

  5. Java NIO和IO的主要差别

    我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景.以及它们怎样影响您的代码设计. Java NIO和IO的主要差别 下表总结了Java N ...

  6. Tensorflow MNIST 数据集測试代码入门

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50614444 測试代码已上传至GitH ...

  7. LicManager系统对各license类型终端客户机器的监控

    与catia软件相似.以下这些软件都是汽车project设计软件.对汽车工业的的研发设计有着不可替代的作用.但它们都有着不同于各自的优势与不足之处. 可是在LicManager许可监控系统下,它们都是 ...

  8. thinkphp5项目--企业单车网站(一)

    thinkphp5项目--企业单车网站(一) 项目地址 fry404006308/BicycleEnterpriseWebsite: Bicycle Enterprise Websitehttps:/ ...

  9. 机器学习(三) Jupyter Notebook, numpy和matplotlib的详细使用 (下)

    七.Numpy中的矩阵运算 八.Numpy中的聚合运算 九.Numpy中的arg运算 十.Numpy中的比较和Fancy Indexing 十一.Matplotlib数据可视化基础 十二.数据加载和简 ...

  10. msiexec

    msiexec: runCmd = new String[]{ "msiexec", "/i", exeName, "/quiet", &q ...