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

    @Overridepublic 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的tagif (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以及mFragmentIdfragment.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_ATTACHFragment 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) {//防止重复commitif (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方法之后,这个标记才会为trueif (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() {@Overridepublic 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 implementsFragmentManager.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的生命周期也有一个很好的认识了

转载于:https://www.cnblogs.com/hanschen-coder/p/6528822.html

Fragment事务管理源码分析相关推荐

  1. REST framework 权限管理源码分析

    REST framework 权限管理源码分析 同认证一样,dispatch()作为入口,从self.initial(request, *args, **kwargs)进入initial() def ...

  2. linux源码文件名,Linux中文件名解析处理源码分析

    Linux中文件名解析处理源码分析 前言 Linux中对一个文件进行操作的时候,一件很重要的事情是对文件名进行解析处理,并且找到对应文件的inode对象,然后创建表示文件的file对象.在此,对文件名 ...

  3. 动态代理原理源码分析

    看了这篇文章非常不错转载:https://www.jianshu.com/p/4e14dd223897 Java设计模式(14)----------动态代理原理源码分析 上篇文章<Java设计模 ...

  4. Linux内核 eBPF基础:kprobe原理源码分析:源码分析

    Linux内核 eBPF基础 kprobe原理源码分析:源码分析 荣涛 2021年5月11日 在 <Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用>中已经介绍了kp ...

  5. Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用示例

    Linux内核 eBPF基础 kprobe原理源码分析:基本介绍与使用示例 荣涛 2021年5月11日 kprobe调试技术是为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术. 利用kpro ...

  6. Linux内核 eBPF基础:Tracepoint原理源码分析

    Linux内核 eBPF基础 Tracepoint原理源码分析 荣涛 2021年5月10日 1. 基本原理 需要注意的几点: 本文将从sched_switch相关的tracepoint展开: 关于st ...

  7. 分享一篇glibc 2.30内存管理源码分析

    分享一篇glibc 2.30内存管理源码分析,出于时间关系文章中可能存在问题(如纰漏.或者解释不顺,后续我会持续更新修正),还请大家海涵,大家互相探讨,也多多希望大家指出文章中问题,我及时斧正.本文只 ...

  8. 分布式事务 TCC-Transaction 源码分析 —— 项目实战

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/TCC-Transaction/http-sample/ 「芋道 ...

  9. 事务回滚什么意思 try_分布式事务 TCC-Transaction 源码分析——事务恢复

    1. 概述 本文分享 TCC 恢复.主要涉及如下二个 package 路径下的类: org.mengyun.tcctransaction.recover RecoverConfig,事务恢复配置接口 ...

  10. 分布式事务 TCC-Transaction 源码分析 —— Dubbo 支持

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/TCC-Transaction/dubbo-support/ 「 ...

最新文章

  1. 刚开始学centos和Oracle
  2. python安装步骤图解-Python安装与卸载流程详细步骤(图解)
  3. cudnn v4安装
  4. stm32CAN波特率计算小程序(QT源码)
  5. 用VBA去除Excel工作表保护密码
  6. java6 3_那些年,一起学的Java 6-3
  7. linux自动更新漏洞,Linux爆本地提权漏洞 请立即更新udev程序
  8. AD PCB板子长度宽度 PCB板子尺寸大小信息
  9. 感受MapXTreme2004
  10. android超级终端使用,android系统超级终端怎么用?
  11. BXP无盘网络方案及设备选用(转)
  12. 小马哥-----高仿苹果6 plus刷机 详细拆机主板图与开机界面图与移植触摸屏版中文rec 界面 多图展示
  13. 澳拳击袋鼠体型巨大 身高超2米体重近200斤
  14. Java金额转换工具类
  15. 从技术角度谈如何开发一款微信联网小游戏
  16. Python 内存回收机制
  17. 南加州大学计算机科学案例,南加州大学计算机科学硕士录取
  18. Network Emulator Toolkit (NEWT) 网络限速工具 (手机和电脑方面)
  19. Drat语法概览(一)
  20. 微型计算机vag是指,《微型计算机复习题》.docx

热门文章

  1. 用于创建此对象的程序是package_21个最佳Flutter软件包,用于简化Flutter应用开发...
  2. ZeroMQ API简介
  3. Linux系统编程 -- 为什么需要进程间通信??
  4. mac docker nginx 配置
  5. ribbon基于接口配置超时_Spring Cloud第二篇:服务消费者RestTemplate+Ribbon
  6. Localdatetime的坑
  7. 记一次spring MVC项目后台重构(持续更新)
  8. python登录网页并操作_python 实现登录网页的操作方法
  9. 记录一个手写场景的过程
  10. 强化学习实战(六)【Windows安装星际争霸Ⅱ 强化学习环境教程】