转载请标明出处:http://blog.csdn.net/shensky711/article/details/53132952

本文出自: 【HansChen的博客】

概述

在Fragment使用中,有时候须要对Fragment进行addremoveshowhidereplace等操作来进行Fragment的显示隐藏等管理。这些管理是通过FragmentTransaction进行事务管理的。事务管理是对于一系列操作进行管理,一个事务包括一个或多个操作命令,是逻辑管理的工作单元。

一个事务開始于第一次运行操作语句,结束于Commit。通俗地将。就是把多个操作缓存起来,等调用commit的时候,统一批处理。以下会对Fragmeng的事务管理做一个代码分析

分析入口

    /**
* 显示Fragment,假设Fragment已加入过,则直接show。否则构造一个Fragment
*
* @param containerViewId 容器控件id
* @param clz Fragment类
*/
protected void showFragment(@IdRes int containerViewId, Class<? extends Fragment> clz) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();//開始事务管理
Fragment f;
if ((f = fm.findFragmentByTag(clz.getName())) == null) {
try {
f = clz.newInstance();
ft.add(containerViewId, f, clz.getName());//加入操作
} catch (Exception e) {
e.printStackTrace();
}
} else {
ft.show(f);//加入操作
}
ft.commit();//提交事务
}

上面是一个简单的显示Fragment的栗子,简单推断一下Fragment是否已加入过,加入过就直接show,否则构造一个Fragment,最后提交事务。

代码分析

FragmentManager



上图是获取FragmentManager的大体过程

要管理Fragment事务。首先是须要拿到FragmentManager。在Activity中能够通过getFragmentManager()方法获取(使用兼容包的话。通过FragmentActivity#getSupportFragmentManager())。在这里我们就不正确兼容包进行分析了

    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

    /**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}

FragmentManager是一个抽象类。它是通过mFragments.getFragmentManager()来获取的。mFragments是FragmentController对象,它通过FragmentController.createController(new HostCallbacks())生成,这是一个静态工厂方法:

    public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}

在这里面直接new了一个FragmentController对象,注意FragmentController的构造方法须要传入一个FragmentHostCallback

FragmentController构造方法

    private final FragmentHostCallback<?> mHost;
private FragmentController(FragmentHostCallback<? > callbacks) {
mHost = callbacks;
}

构造方法非常easy,传入了一个FragmentHostCallback实例

FragmentController#getFragmentManager

    public FragmentManager getFragmentManager() {
return mHost.getFragmentManagerImpl();
}

这里又调用了mHost的getFragmentManagerImpl方法。希望童鞋们没有被绕晕,mHost是一个FragmentHostCallback实例。那我们回过头来看看它传进来的地方

FragmentHostCallback

这个FragmentHostCallback是一个抽象类,我们能够看到,在Activity中是传入了 Activity#HostCallbacks内部类,这个就是FragmentHostCallback的实现类

FragmentHostCallback#getFragmentManagerImpl

    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}

最终找到FragmentManager的真身FragmentManagerImpl

FragmentManagerImpl#beginTransaction

    @Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}

能够看到,所谓的FragmentTransaction事实上就是一个BackStackRecord。到如今。FragmentManager和FragmentTransaction我们都找到了。下图就是各个类之间的关系:

以下開始真正的事务管理分析,我们先选择一个事务add来进行分析

FragmentTransaction#add

    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
} private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { //设置fragment的FragmentManagerImpl,mManager事实上就是Activity#HostCallbacks中的成员变量
fragment.mFragmentManager = mManager; //设置fragment的tag
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("...");
}
fragment.mTag = tag;
} if (containerViewId != 0) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("...");
}
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("");
}
//设置fragment的mContainerId以及mFragmentId
fragment.mContainerId = fragment.mFragmentId = containerViewId;
} //新增一个操作
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
//加入操作
addOp(op);
} //插入到链表的最后
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++;
}

add的操作步骤为:

  1. 设置fragment的FragmentManagerImpl
  2. 设置fragment的tag
  3. 设置fragment的mContainerId以及mFragmentId
  4. 插入一个类型为OP_ADD的操作到链表最后

这里用到了一个类:

    static final class Op {
Op next;//下一操作节点
Op prev;//上一操作节点
int cmd;//操作类型,可选有:OP_NULL|OP_ADD|OP_REPLACE|OP_REMOVE|OP_HIDE|OP_SHOW|OP_DETACH|OP_ATTACH
Fragment fragment;//操作的Fragment对象
int enterAnim;//入场动画
int exitAnim;//出场动画
int popEnterAnim;//弹入动画
int popExitAnim;//弹出动画
ArrayList<Fragment> removed;
}

这是一个操作链表节点。

全部add、remove、hide等事物最终会形成一个操作链

FragmentTransaction#commit

等全部操作都插入后,最后我们须要调用FragmentTransaction的commit方法。操作才会真正地运行。

    public int commit() {
return commitInternal(false);
} int commitInternal(boolean allowStateLoss) {
//防止反复commit
if (mCommitted) {
throw new IllegalStateException("commit already called");
} //DEBUG代码统统无论
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
PrintWriter pw = new FastPrintWriter(logw, false, 1024);
dump(" ", null, pw, null);
pw.flush();
} mCommitted = true; //仅仅有调用了addToBackStack方法之后,这个标记才会为true
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
//插入事物队列
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}

FragmentManagerImpl#enqueueAction

    /**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}

这里把操作加入到mPendingActions列表里去。并通过mHost.getHandler()获取Handler发送运行请求。从上面的分析知道。mHost就是Activity的HostCallbacks。构造方法中把Activity的mHandler传进去了,这里运行的mHost.getHandler()获取到的也就是Activity中的mHandler,这样做是由于须要在主线程中运行

final Handler mHandler = new Handler();

再看看mExecCommit中做了什么操作:

    Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
}; /**
* Only call from main thread!
*/
public boolean execPendingActions() {
if (mExecutingActions) {
throw new IllegalStateException("Recursive entry to executePendingTransactions");
} //再次检測是否主线程
if (Looper.myLooper() != mHost.getHandler().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) {
break;
} numActions = mPendingActions.size();
if (mTmpActions == null || mTmpActions.length < numActions) {
mTmpActions = new Runnable[numActions];
} mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mHost.getHandler().removeCallbacks(mExecCommit);
} mExecutingActions = true;
//遍历运行待处理的事务操作
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
} doPendingDeferredStart(); return didSomething;
}

插入了事物之后,就是在主线程中把须要处理的事务统一处理。处理事务是通过运行mTmpActions[i].run()进行的,这个mTmpActions[i]就是前面我们通过enqueueAction方法插入的BackStackRecord,童鞋们可能没注意到,它但是一个Runnable,我们来看看它的定义

final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, Runnable {
static final String TAG = FragmentManagerImpl.TAG; ... ...
}

兜兜转转,我们又回到了BackStackRecord

BackStackRecord#run

    public void run() {

        ......

        if (mManager.mCurState >= Fragment.CREATED) {
SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
calculateFragments(firstOutFragments, lastInFragments);
beginTransition(firstOutFragments, lastInFragments, false);
}
//遍历链表。依据cmd事务类型依次处理事务
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;
int containerId = f.mContainerId;
if (mManager.mAdded != null) {
for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
Fragment old = mManager.mAdded.get(i);
if (old.mContainerId == containerId) {
if (old == f) {
op.fragment = f = null;
} else {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old);
old.mNextAnim = op.exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
}
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 new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
} op = op.next;
} mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true); if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}

到这一步,提交的事务就被真正运行了,我们知道。即使commit了事务之后,也不是同步运行的,是通过Handler发送到主线程运行的。

全部事务的处理都是在run方法里面运行。但是我们留意到,想要搞清楚add、remove等事务背后真正做了什么。还须要深入了解FragmentManagerImpl。

本文主要解说Fragment事务的流程,FragmentManagerImpl的分析准备放到下一篇分析文章「Fragment源代码分析」中,相信通过分析之后,就能够对Fragment的生命周期也有一个非常好的认识了

Fragment事务管理源代码分析的更多相关文章

  1. Spring事务管理全面分析

    Spring 事务属性分析什么是事物  事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常 ...

  2. [Android]Fragment源代码分析(三) 事务

    Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩.我们先引入一个简单经常使用的Fragment事务管理代码片段: FragmentTransaction ft = thi ...

  3. 全面分析 Spring 的编程式事务管理及声明式事务管理

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  4. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  5. redis 源代码分析(一) 内存管理

    一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...

  6. 【转】Spring事务管理

    原文链接 在 Spring 中,事务是通过 TransactionDefinition 接口来定义的.该接口包含与事务属性有关的方法.具体如清单 1 所示: 清单 1. TransactionDefi ...

  7. spring事务管理——编程式事务、声明式事务

    本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 ...

  8. Spring编程式事务管理及声明式事务管理

    本文将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. Spring 事务属性分析 事务管理 ...

  9. Spring 简单而强大的事务管理功能

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

随机推荐

  1. or in 、Object.keys()以及Object.getOwnPropertyNames有什么区别?

    or in .Object.keys()以及Object.getOwnPropertyNames的区别 var obj= Object.create(parent, { b: { value: 2, ...

  2. 【Henu ACM Round#15 E】 A and B and Lecture Rooms

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 最近公共祖先. (树上倍增 一开始统计出每个子树的节点个数_size[i] 如果x和y相同. 那么直接输出n. 否则求出x和y的最近 ...

  3. sleep实现原理

    用户程序中的睡眠: sleep()    usleep()    nanosleep() sleep()和nanosleep()都是使进程睡眠一段时间后被唤醒,但是二者的实现完全不同.Linux中并没 ...

  4. ZOJ Problem Set - 3822Domination(DP)

    ZOJ Problem Set - 3822Domination(DP) problemCode=3822">题目链接 题目大意: 给你一个n * m的棋盘,每天都在棋盘上面放一颗棋子 ...

  5. hdu 1875 畅通project再续(kruskal算法计算最小生成树)

    畅通project再续 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  6. 用Vue.js来实现城市三级联动

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  7. 负载均衡之lvs

    集群(cluster):将一组计算机软/硬件连接起来,高度紧密的协作完成计算工作,其中的单个计算机通常称为节点.负载均衡集群(Load Balancing):通过负载均衡器,将负载尽可能平均分摊处理. ...

  8. 微信小程序从零开始开发步骤(一)搭建开发环境

    从零到有写一个小程序系列专题,很早以前就想写来分享,但由于项目一直在进展,没有过多的时间研究技术,现在可以继续分享了. 1:注册 用没有注册过微信公众平台的邮箱注册一个微信公众号, 申请帐号 ,网址: ...

  9. 03006_DOS操作数据乱码解决

    1.我们在dos命令行操作中文时,会报错 insert into sort(sid,sname) values(2,"电视机"); ERROR 1366 (HY000): Inco ...

  10. 【Uva 1289】Stacking Plates

    [Link]: [Description] 有n(1≤n≤50)堆盘子,第i堆盘子有hi个盘子(1≤hi≤50),从上到下直径不减.所有盘 子的直径均不超过10000.有如下两种操作. split:把 ...