Fragmentation 源码分析

Fragmentation 源码分析


前言

前段时间突然发觉,Fragmentation 这个库我已经使用了这么久了,从 0.10.1 版本开始接触,用到现在已经 1.3.x 了。这是一段很长的时间,自己在这段时间里也成长了不少。其实从使用 Fragmentation 这个库以来,这个库虽然好用,但是一直也没有看过它的源码,不知道内部实现,挺惭愧的,这段时间正好有点空闲时间,就分析一波它的源码吧,给自己一个交代。由于本人水平有限,又哪里写的不对的地方还请即时帮我指正~


简介

它是一个对 fragment 进行管理的三方库,可以方便地对 fragment 进行 进栈、出栈、显示隐藏等操作,只需要调用简单的 api;对 fragment 的生命周期进行了拓展;方便的实现单 Activity + 多 Fragment 的 app 架构
GitHub:https://github.com/YoKeyword/Fragmentation


常用 API 分析

对于源码分析,一般来说一个三方库的源码是非常庞大非常复杂的,最好的分析方式是从某个功能开始入手,去分析它的调用栈,看它从 api 调用到实现功能之间,在源码里都做了哪些操作。本文就来分析一下 Fragmentation 中常用的 loadRootFragment、start、pop api。

本文基于 Fragmentation 1.3.6 版本源码分析

loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)

它是 SupportActivity/SupportFragment 中的一个方法,加载根Fragment, 即Activity内的第一个Fragment 或 Fragment内的第一个子Fragment

  • 先从 SupportActivity 中的 loadRootFragment api 进行分析

    1. SupportActivity#loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)

      image

      它直接调用了 SupportActivityDelegateloadRootFragment(int containerId, ISupportFragment toFragment) 方法。可以看出 SupportActivityDelegate 其实是 SupportActivity 的代理类,真正的逻辑代码应该都在代理类里边。

    2. SupportActivityDelegate#loadRootFragment(int containerId, ISupportFragment toFragment)

      image

      这里到最后调用了 TransactionDelegate#loadRootTransaction(final FragmentManager fm, final int containerId, final ISupportFragment to, final boolean addToBackStack, final boolean allowAnimation) 方法。

    3. TransactionDelegate#loadRootTransaction(final FragmentManager fm, final int containerId, final ISupportFragment to, final boolean addToBackStack, final boolean allowAnimation)

      image

      这里把对 fragment 的操作封装成一个 Action,并把 Action 放入一个队列里边,当此 Action 前边没有排队的 Action 后,就会执行它,调用 TransactionDelegate#start(FragmentManager fm, final ISupportFragment from, ISupportFragment to, String toFragmentTag, boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type) 方法。

      这个 ActionQueue 就是这个库的核心,它把所有的对 fragment 的操作,都放到了这个队列中去顺序执行,避免了自己手动操作 fragment 带来的一些隐患。

    4. TransactionDelegate#start(FragmentManager fm, final ISupportFragment from, ISupportFragment to, String toFragmentTag, boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type)

      image

      到此,fragment 就会显示出来了,调用栈结束。

  • 从 SupportFragment 中的 loadRootFragment api 进行分析

    从上边分析 SupportActivity 可以推测,SupportFragment 里边应该也有一个代理类,去真正的处理代码逻辑。而在这个代理类中也持有事务代理类的实例,关于 fragment 的操作都交给事务代理类处理。

    1. SupportFragment#loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)

      看源码可知,这个方法内部直接调用了 SupportFragmentDelegate 代理类的 loadRootFragment(int containerId, @NonNull ISupportFragment toFragment) 方法。

    2. SupportFragmentDelegate#loadRootFragment(int containerId, ISupportFragment toFragment)

      private TransactionDelegate mTransactionDelegate;
      
      /**
       * 加载根Fragment, 即Activity内的第一个Fragment 或 Fragment内的第一个子Fragment
       *
       * @param containerId 容器id
       * @param toFragment  目标Fragment
       */
      public void loadRootFragment(int containerId, ISupportFragment toFragment) {
          mDelegate.loadRootFragment(containerId, toFragment);
      }
      
      public void loadRootFragment(int containerId, ISupportFragment toFragment, boolean addToBackStack, boolean allowAnim) {
          mDelegate.loadRootFragment(containerId, toFragment, addToBackStack, allowAnim);
      }
      
      

      可以看到,SupportFragmentDelegate 里边也是持有了 TransactionDelegate 的实例,这之后的调用栈就跟在 SupportActivity 中调用是一样的了,就不继续分析了。

通过上边的分析,现在我们知道了 SupportActivitySupportFragment 内部分别有 SupportActivityDelegateSupportFragmentDelegate 代理类,所有的操作都直接交给了代理类去处理。而这两个代理类都分别持有 TransactionDelegate 的实例,所有的关于 fragment 的操作又会都交给它去处理。所以接下来对于 start、pop api 的分析,只分析 SupportActivitySupportFragment 其中一个就行了。

start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)

作用:启动/打开 一个 fragment

这里只分析 SupportActivity 里边的调用栈

  1. SupportActivity#start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)

    image

  2. SupportActivityDelegate#start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)

    image

  3. TransactionDelegate#dispatchStartTransaction(final FragmentManager fm, final ISupportFragment from, final ISupportFragment to, final int requestCode, final int launchMode, final int type)

    image

  4. TransactionDelegate#doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type)

    image

    后边调用的 start 方法,完成了 fragment 往容器里边的加载。至此整个调用流程就结束了。

pop()

作用:关闭/退出 一个 fragment

这里只分析 SupportActivity 里边的调用栈

  1. SupportActivity#pop()

    image

  2. SupportActivityDelegate#pop()

    image

  3. TransactionDelegate#pop(final FragmentManager fm)

    image

  4. TransactionDelegate#removeTopFragment(FragmentManager fm)

    private void removeTopFragment(FragmentManager fm) {
        try { // Safe popBackStack()
            ISupportFragment top = SupportHelper.getBackStackTopFragment(fm);
            if (top != null) {
                fm.beginTransaction()
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
                        .remove((Fragment) top)
                        .commitAllowingStateLoss();
            }
        } catch (Exception ignored) {
    
        }
    }
    

    拿到栈顶 fragment,利用 FragmentManager 关闭栈顶 fragment。调用链结束。


此 Lib 的整体结构

通过上边对源码的大致分析,基本已经可以看清楚它的整体结构了:当我们在 Activity 或 Fragment 中直接调用它的 API 的时候,它通过对应的代理类 SupportActivityDelegateSupportFragmentDelegate,把代码逻辑转换到代理类中;而这两个代理类中都持有 TransactionDelegate 的实例,对 fragment 的操作逻辑又都转移到 TransactionDelegate 中去完成;在 TransactionDelegate 中有一个任务队列(ActionQueue),所有的操作都先封装成一个任务(Action),然后放入队列中等待被操作。


总结

通过上边的分析,Fragmentation 的整体结构看起来清晰了很多,收获还是挺多的,从只会用到了解了大致原理,有种豁然开朗的感觉。其实分析到现在,只是了解了这个库的核心-对 fragment 的操作。还有一些东西应该也挺有趣的,比如:对 fragment 生命周期的拓展 onSupportVisibleonSupportInvisible、fragment 转场动画的实现、启动模式的处理、debug 模式的任务栈显示等等。之后时间充足的话,可以再分析一下。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
UDP分片是指将一个过大的UDP数据报分割成多个IPv4片段的过程。UDP分片卸载允许设备将超大的UDP数据报分割成多个IPv4片段。与TCP分段卸载的要求类似,但是对于分片后的IPv4片段,其IPv4标识符不应该递增,因为它们属于同一个UDP数据报的分片。\[1\] 关于UDP分片卸载的更多信息可以参考文档\[2\]。在链路层中,有一个最大传输单元(MTU)的概念,它限制了数据帧的最大长度。不同网络类型的MTU值不同,例如以太网的MTU是1500字节。当IP层需要传输的数据包长度超过MTU时,就需要对数据包进行分片操作,使每个分片的长度小于或等于MTU。UDP分片就是在这种情况下发生的。\[3\] 总结来说,UDP分片是将超大的UDP数据报分割成多个IPv4片段的过程,以适应链路层的MTU限制。每个分片都包含有关它们属于同一个UDP数据报的信息。 #### 引用[.reference_title] - *1* *2* [Kernel: net: udp: ufo,UDP fragment offload, UDP分片脱负](https://blog.csdn.net/qq_36428903/article/details/126394978)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [UDP可靠传输(KCP))](https://blog.csdn.net/asdaqqwc/article/details/122385671)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值