热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析FragmentTransaction的源码实现机制

在探讨Fragment的使用时,FragmentTransaction是不可或缺的一部分。作为管理Fragment操作的核心类,FragmentTransaction提供了诸如显示、隐藏、添加和移除等方法,这些方法在实际开发中被广泛使用。本文将深入解析FragmentTransaction的源码实现机制,帮助开发者更好地理解和优化Fragment的管理。通过分析其内部工作原理,读者可以掌握如何高效地进行Fragment的动态管理和性能优化。

谈到fragment的使用,肯定绕不过FragmentTransaction事务,对fragment的操作必定用到它,其提供show,hide,add,remove,replace等常用的fragment操作,最后commit操作,这么强大的管理类,它内部是如何实现的呢?为什么可以连续调用多个api,最后一次要commit操作?

1、创建FragmentTransaction对象:

FragmentTransaction ft = mFragmentManager.beginTransaction();

源码实现:

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

可以看到new 出一个新的 BackStackRecord对象,这个BackStackRecord就是api的真正实现者。

我以remove fragment这个操作看下内部实现:

@Overridepublic FragmentTransaction remove(Fragment fragment) {Op op = new Op();op.cmd = OP_REMOVE;op.fragment = fragment;addOp(op);return this;}

这个Op是什么东东呢?,又把op加入到addOp中,我们不妨先看下这个addOp的实现:

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++;}

这段代码还是比较容易理解的,其实remove操作,就是新建一个 链表结点,结点中保存了当前的操作,保存了fragment的对象引用,所以 FragmentTransaction的内部主要实现就是通过链表操作的,每个链表结点保存了每一个api操作的信息,好了,我们看下commit的实现:

@Overridepublic int commit() {return commitInternal(false);}

只能commit一次,否则抛出异常

int commitInternal(boolean allowStateLoss) {if (mCommitted) throw new IllegalStateException("commit already called");if (FragmentManagerImpl.DEBUG) {Log.v(TAG, "Commit: " + this);LogWriter logw = new LogWriter(TAG);PrintWriter pw = new PrintWriter(logw);dump(" ", null, pw, null);}mCommitted = true;if (mAddToBackStack) {mIndex = mManager.allocBackStackIndex(this);} else {mIndex = -1;}mManager.enqueueAction(this, allowStateLoss);return mIndex;}

看下 mManager.enqueueAction(this, allowStateLoss)的实现:

/*** 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();}mPendingActions.add(action);if (mPendingActions.size() == 1) {mHost.getHandler().removeCallbacks(mExecCommit);mHost.getHandler().post(mExecCommit);}}}

代码解读:
1、先去检查 checkStateLoss 状态,正常commit操作,是肯定去检查的,如果是在onSaveInstanceState状态保存之后,再去commit操作,肯定会报错。

2、BackStackRecord 类实现了 Runnable,看其run方法。

public void run() {// 省略部分代码// 指向链表头指针Op op = mHead;// 遍历链表结点while (op != null) {//每一个结点的cmd操作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 (FragmentManagerImpl.DEBUG) {Log.v(TAG,"OP_REPLACE: adding=" + f + " old=" + old);}if (old.mContainerId == containerId) {if (old == f) {op.fragment = f = null;} else {if (op.removed == null) {op.removed = new ArrayList();}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 new IllegalArgumentException("Unknown cmd: " + op.cmd);}}//指向下一个结点op = op.next;}mManager.moveToState(mManager.mCurState, mTransition,mTransitionStyle, true);// 是否调用了addBackStack(),如果加入,将当前的BackStackRecord加入到栈中if (mAddToBackStack) {mManager.addBackStackState(this);}}

这段代码解读:

1、每次commit之后,都会commit一个runnable任务,run()方法里面 对链表进行遍历操作,从头结点开始,依次访问每个结点,然后读取里面每一个结点的cmd,执行相应的方法。注意replace,是先remove,后add。
2、如果调用了addBackStack()方法,会将当前的任务对象加入到栈中。

void addBackStackState(BackStackRecord state) {if (mBackStack == null) {mBackStack = new ArrayList();}mBackStack.add(state);reportBackStackChanged();}

可见我的这篇博客 从源码角度解释 fragment 坑(一)

我在想,FragmentTransaction 事务内部为什么要实现链表操作呢?

如果单纯的只是commit之前调用几个api,hide,show我认为可能没有必要,它的必要性,就是在退栈的时候,能够记住你之前的每个结点的行为,并且进行相应的反操作,比如我 先用事务,hide 1,remove 2,add 3,甚至更多,退栈的时候,我们是不是先执行,remove 3,往往3走到onDestroyview方法,2 调用 onCreateView,show 1出来,如果不用链表的操作的话,确实不太方便。


推荐阅读
  • MongoDB高可用架构:深入解析Replica Set机制
    MongoDB的高可用架构主要依赖于其Replica Set机制。Replica Set通过多个mongod节点的协同工作,实现了数据的冗余存储和故障自动切换,确保了系统的高可用性和数据的一致性。本文将深入解析Replica Set的工作原理及其在实际应用中的配置和优化方法,帮助读者更好地理解和实施MongoDB的高可用架构。 ... [详细]
  • 如何使用 net.sf.extjwnl.data.Word 类及其代码示例详解 ... [详细]
  • 深入探索Node.js新框架:Nest.js第六篇
    在本文中,我们将深入探讨Node.js的新框架Nest.js,并通过一个完整的示例来展示其强大功能。我们将使用多个装饰器创建一个基本控制器,该控制器提供了多种方法来访问和操作内部数据,涵盖了常见的CRUD操作。此外,我们还将详细介绍Nest.js的核心概念和最佳实践,帮助读者更好地理解和应用这一现代框架。 ... [详细]
  • C#中实现高效UDP数据传输技术
    C#中实现高效UDP数据传输技术 ... [详细]
  • 本文介绍了Android动画的基本概念及其主要类型。Android动画主要包括三种形式:视图动画(也称为补间动画或Tween动画),主要通过改变视图的属性来实现动态效果;帧动画,通过顺序播放一系列预定义的图像来模拟动画效果;以及属性动画,通过对对象的属性进行平滑过渡来创建更加复杂的动画效果。每种类型的动画都有其独特的应用场景和实现方式,开发者可以根据具体需求选择合适的动画类型。 ... [详细]
  • 本文详细介绍了使用响应文件在静默模式下安装和配置Oracle 11g的方法。硬件要求包括:内存至少1GB,具体可通过命令`grep -i memtotal /proc/meminfo`进行检查。此外,还提供了详细的步骤和注意事项,确保安装过程顺利进行。 ... [详细]
  • Go语言实现Redis客户端与服务器的交互机制深入解析
    在前文对Godis v1.0版本的基础功能进行了详细介绍后,本文将重点探讨如何实现客户端与服务器之间的交互机制。通过具体代码实现,使客户端与服务器能够顺利通信,赋予项目实际运行的能力。本文将详细解析Go语言在实现这一过程中的关键技术和实现细节,帮助读者深入了解Redis客户端与服务器的交互原理。 ... [详细]
  • 在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python 的 `asyncio` 模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析 `asyncio` 模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。 ... [详细]
  • 本文深入探讨了NDK与JNI技术在实际项目中的应用及其学习路径。通过分析工程目录结构和关键代码示例,详细介绍了如何在Android开发中高效利用NDK和JNI,实现高性能计算和跨平台功能。同时,文章还提供了从基础概念到高级实践的系统学习指南,帮助开发者快速掌握这些关键技术。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • 在 Vbox 和 Hbox 布局中,当用户点击容器添加一个矩形时,系统会自动为该矩形分配坐标并打印其位置信息。此外,在按键事件触发时,系统仅打印当前矩形的坐标值。这两种布局在特定的交互场景下,能够动态地管理和更新子组件的位置。 ... [详细]
  • 开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用
    开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用 ... [详细]
  • 期末Web开发综合实践项目:运用前端技术打造趣味小游戏体验
    期末Web开发综合实践项目中,学生通过运用HTML、CSS和JavaScript等前端技术,设计并实现了一款趣味性十足的小游戏。该项目不仅检验了学生对前端基础知识的掌握情况,还提升了他们的实际操作能力和创意设计水平。视频链接展示了项目的最终成果,直观呈现了游戏的互动性和视觉效果。 ... [详细]
  • SpringBoot启动脚本详解:BAT文件应用与基础入门指南(SpringBoot系列第1篇)
    如果你还在为SSM框架的复杂搭建过程和繁琐的配置文件而烦恼,那么SpringBoot将是你的一大福音。作为SpringBoot系列的第一篇文章,本文详细介绍了如何使用BAT文件来启动SpringBoot应用,并提供了基础入门指南,帮助开发者快速上手,简化开发流程。 ... [详细]
  • 掌握DSP必备的56个核心问题,我已经将其收藏以备不时之需! ... [详细]
author-avatar
Yyao
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有