概述

这篇文章的简要分析了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 中的property

    """ property() 的第一个参数是 getter 方法,第二个参数是 setter 方法 xx = property(a,b) @property #用于指示g ...

  2. 走进ReactiveCocoa的世界

    在学习ReactiveCocoa之前,先学习一下概念 ReactiveCocoa 是一套开源的基于Cocoa的FRP框架 .FRP的全称是Functional Reactive Programming ...

  3. Chromium Graphics: Aura

    Aura (obsolete) This document is still good for a high level overview, with contact information, but ...

  4. pycaffe 可视化常用

    net.params['layername'].[0]/[1] caffe的一个程序跑完之后会在snapshot所指定的目录下产生一个后缀名为caffemode的文件,这里存放的就是我们在训练网络的时 ...

  5. 洛谷 P1368 工艺 后缀自动机 求最小表示

    后缀自动机沙茶题 将字符串复制一次,建立后缀自动机. 在后缀自动机上贪心走 $n$ 次即可. Code: #include <cstdio> #include <algorithm& ...

  6. Unity Camera中心点的偏移

    在VR 中,如果镜片的中心轴,和屏幕的中心轴不在一条线上, 就会出现无论如何调节IPD,看到的图像都不清晰,这时候,要修改Camera的投影矩阵, 只需要一句代码就能搞定: Camera.main.p ...

  7. [React] Make Controlled React Components with Control Props

    Sometimes users of your component want to have more control over what the internal state is. In this ...

  8. POJ 3070 Fibonacci 矩阵高速求法

    就是Fibonacci的矩阵算法.只是添加一点就是由于数字非常大,所以须要取10000模,计算矩阵的时候取模就能够了. 本题数据不强,只是数值本来就限制整数,故此能够0ms秒了. 以下程序十分清晰了, ...

  9. CentOS 中使用yum出现的“UnicodeDecodeError: &#39;ascii&#39; codec”问题解决方法

    问题 新装了CentOS 6.5系统,打算使用yum安装程序是出现了例如以下错误: Loading mirror speeds from cached hostfile Traceback (most ...

  10. Effective C++ 条款13

    以对象管理资源 资源的种类非常多,动态分配的内存.文件描写叙述器.相互排斥锁.图像界面中画刷.数据库连接.网络socket等. 资源通常是有限的.当你不用时,必须释放.不然就会造成资源浪费.更严重的情 ...