转载请标明出处: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;
}

构造方法很简单,传入了一个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事务管理方式 依据Spring.xsd文件可以发现,Spring提供了advice,annotation-driven,jta-transaction-manager3种事务管理方式.详情 ...

  2. java 1.8 动态代理源码分析

    JDK8动态代理源码分析 动态代理的基本使用就不详细介绍了: 例子: class proxyed implements pro{ @Override public void text() { Syst ...

  3. Spring Boot自动装配原理源码分析

    1.环境准备 使用IDEA Spring Initializr快速创建一个Spring Boot项目 添加一个Controller类 @RestController public class Hell ...

  4. Flask上下文管理源码分析 ——(3)

    引出的问题 Flask如何使用上下文临时把某些对象变为全局可访问 首先我们做如下的几种情况的假设 情况一:单进程单线程 这种情况可以基于全局变量存储临时的对象 情况二:单进程多线程 这种情况会出现多个 ...

  5. JDK7动态代理源码分析

    IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), new Class[]{IObject ...

  6. (转)Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义

    1.从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,注解(Annotation)是JDK1.5中引入的一个新特性,用于简化Bean的配置,某些场合可以 ...

  7. rt-thread的定时器管理源码分析

    1 前言 rt-thread可以采用软件定时器或硬件定时器来实现定时器管理的,所谓软件定时器是指由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务.而硬件 ...

  8. spring事务管理源码解析--加了@Transactional注解后Spring究竟为我们做了哪些事情?

    大家都知道事务管理是基于AOP的,对AOP还不了解的请自行百度. 实现一个事务需要以下几步:1.获取数据库连接  2.执行数据库操作  3.如果2步骤发生异常就回滚,否则就提交  4.释放资源. 然后 ...

  9. Spring 与 MyBatis 事务管理源码解析

    用到mybatis便由spring和myabtis集成,SqlSessionFactoryBean(直接负责对mybatis所需环境的创建) ,配置相应的datasource到springConfig ...

随机推荐

  1. django-模板之include标签(十五)

    就是将一些常用的html代码分离出来,使其可以重复利用,减少代码量 index.html <!DOCTYPE html> <html lang="en"> ...

  2. json基本内容

    json的基本信息和历史 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于欧洲计算机协会制定的js规范的一个子集,采用完全独立于编程语言的文本格式来 ...

  3. 启动elasticsearch报错的几种原因及解决方法

    ERROR: [1] bootstrap checks failed [1]: max virtual memory areas vm.max_map_count [65530] is too low ...

  4. Node配合WebSocket做多文件下载以及进度回传

    起因 为什么做这个东西,是突然间听一后端同事说起Annie这个东西,发现这个东西下载视频挺方便的,会自动爬取网页中的视频,然后整理成列表.发现用命令执行之后是下面的样子: 心里琢磨了下,整一个界面玩一 ...

  5. linux lsof 查看进程打开那些文件 或者 查看文件给那个进程使用

    lsof命令是什么? 可以列出被进程所打开的文件的信息.被打开的文件可以是 1.普通的文件,2.目录  3.网络文件系统的文件,4.字符设备文件  5.(函数)共享库  6.管道,命名管道 7.符号链 ...

  6. CSPS模拟 46

    勿忘国耻. 由于重新评测我看到了不是很真实的一幕 紧接着是更不真实的一幕 就在虚假形象快要建立完成的时候 它由于来自东方的神秘力量倒塌了 被两个学校的大佬爆踩了(捂脸 T1 无脑背包? 考试时想1h想 ...

  7. NOIP模拟 5

    考试的时候相当浮躁,而且脑子并不工作 T1看了几眼,觉得没思路,先skip T2一打眼,满足条件的最大值,二分!(然后就死了,根本没想有没有单调性) T3找了半天规律,一开始自己手模的K3都过不了样例 ...

  8. 【工利其器】Android Lint篇——为Android量身定做的静态代码审查工具

    前言 我们在进行代码优化的时候,往往是通过开发者的经验来判断哪些代码可能存在潜在问题,哪些资源的使用不合规范等.实际上Android SDK提供了一款功能非常强大的工具,来帮助开发者自动检测代码的质量 ...

  9. 基于华为物联网IOT的应用开发 ---界面管理开发

    在前面随笔<基于华为物联网IOT的应用开发 --- 基于.net 的SDK封装>介绍过IOT中应用侧SDK的封装,主要就是基于华为IOT的应用侧封装,以便在应用系统中进行调用.应用侧SDK ...

  10. 通俗易懂了解Vue组件的生命周期

    1.前言 在使用vue2.0进行日常开发中,我们总有这样的需求,我就想在页面刚一加载出这个表格组件时就发送请求去后台拉取数据,亦或者我想在组件加载前显示个loading图,当组件加载出来就让这个loa ...