热门标签 | 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出来,如果不用链表的操作的话,确实不太方便。


推荐阅读
  • 当unique验证运到图片上传时
    2019独角兽企业重金招聘Python工程师标准model:public$imageFile;publicfunctionrules(){return[[[na ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 黑马头条项目:Vue 文章详情模块与交互功能实现
    本文详细介绍了如何在黑马头条项目中配置文章详情模块的路由、获取和展示文章详情数据,以及实现关注、点赞、不喜欢和评论功能。通过这些步骤,您可以全面了解如何开发一个完整的前端文章详情页面。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 本文将详细介绍如何在没有显示器的情况下,使用Raspberry Pi Imager为树莓派4B安装操作系统,并进行基本配置,包括设置SSH、WiFi连接以及更新软件源。 ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
  • Django Token 认证详解与 HTTP 401、403 状态码的区别
    本文详细介绍了如何在 Django 中配置和使用 Token 认证,并解释了 HTTP 401 和 HTTP 403 状态码的区别。通过具体的代码示例,帮助开发者理解认证机制及权限控制。 ... [详细]
  • 本文旨在探讨如何利用决策树算法实现对男女性别的分类。通过引入信息熵和信息增益的概念,结合具体的数据集,详细介绍了决策树的构建过程,并展示了其在实际应用中的效果。 ... [详细]
  • 2018-2019学年第六周《Java数据结构与算法》学习总结
    本文总结了2018-2019学年第六周在《Java数据结构与算法》课程中的学习内容,重点介绍了非线性数据结构——树的相关知识及其应用。 ... [详细]
  • 本文介绍如何使用MFC和ADO技术调用SQL Server中的存储过程,以查询指定小区在特定时间段内的通话统计数据。通过用户界面选择小区ID、开始时间和结束时间,系统将计算并展示小时级的通话量、拥塞率及半速率通话比例。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
  • 本文详细介绍如何使用 Python 集成微信支付的三种主要方式:Native 支付、APP 支付和 JSAPI 支付。每种方式适用于不同的应用场景,如 PC 网站、移动端应用和公众号内支付等。 ... [详细]
  • 本文介绍了在JSP页面中显示用户登录时间的几种常见方法,包括直接使用Date对象、格式化日期输出以及使用SimpleDateFormat类进行更精确的时间显示。 ... [详细]
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社区 版权所有