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 进行分析
-
SupportActivity#loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)
它直接调用了
SupportActivityDelegate
的loadRootFragment(int containerId, ISupportFragment toFragment)
方法。可以看出SupportActivityDelegate
其实是SupportActivity
的代理类,真正的逻辑代码应该都在代理类里边。 -
SupportActivityDelegate#loadRootFragment(int containerId, ISupportFragment toFragment)
这里到最后调用了
TransactionDelegate#loadRootTransaction(final FragmentManager fm, final int containerId, final ISupportFragment to, final boolean addToBackStack, final boolean allowAnimation)
方法。 -
TransactionDelegate#loadRootTransaction(final FragmentManager fm, final int containerId, final ISupportFragment to, final boolean addToBackStack, final boolean allowAnimation)
这里把对 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 带来的一些隐患。 -
TransactionDelegate#start(FragmentManager fm, final ISupportFragment from, ISupportFragment to, String toFragmentTag, boolean dontAddToBackStack, ArrayList<TransactionRecord.SharedElement> sharedElementList, boolean allowRootFragmentAnim, int type)
到此,fragment 就会显示出来了,调用栈结束。
-
-
从 SupportFragment 中的 loadRootFragment api 进行分析
从上边分析 SupportActivity 可以推测,SupportFragment 里边应该也有一个代理类,去真正的处理代码逻辑。而在这个代理类中也持有事务代理类的实例,关于 fragment 的操作都交给事务代理类处理。
-
SupportFragment#loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)
看源码可知,这个方法内部直接调用了
SupportFragmentDelegate
代理类的loadRootFragment(int containerId, @NonNull ISupportFragment toFragment)
方法。 -
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
中调用是一样的了,就不继续分析了。
-
通过上边的分析,现在我们知道了 SupportActivity
和 SupportFragment
内部分别有 SupportActivityDelegate
和 SupportFragmentDelegate
代理类,所有的操作都直接交给了代理类去处理。而这两个代理类都分别持有 TransactionDelegate
的实例,所有的关于 fragment 的操作又会都交给它去处理。所以接下来对于 start、pop
api 的分析,只分析 SupportActivity
或 SupportFragment
其中一个就行了。
start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)
作用:启动/打开 一个 fragment
这里只分析 SupportActivity
里边的调用栈
-
SupportActivity#start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)
-
SupportActivityDelegate#start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode)
-
TransactionDelegate#dispatchStartTransaction(final FragmentManager fm, final ISupportFragment from, final ISupportFragment to, final int requestCode, final int launchMode, final int type)
-
TransactionDelegate#doDispatchStartTransaction(FragmentManager fm, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type)
后边调用的 start 方法,完成了 fragment 往容器里边的加载。至此整个调用流程就结束了。
pop()
作用:关闭/退出 一个 fragment
这里只分析 SupportActivity
里边的调用栈
-
SupportActivity#pop()
-
SupportActivityDelegate#pop()
-
TransactionDelegate#pop(final FragmentManager fm)
-
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 的时候,它通过对应的代理类 SupportActivityDelegate
和 SupportFragmentDelegate
,把代码逻辑转换到代理类中;而这两个代理类中都持有 TransactionDelegate
的实例,对 fragment 的操作逻辑又都转移到 TransactionDelegate
中去完成;在 TransactionDelegate
中有一个任务队列(ActionQueue),所有的操作都先封装成一个任务(Action),然后放入队列中等待被操作。
总结
通过上边的分析,Fragmentation 的整体结构看起来清晰了很多,收获还是挺多的,从只会用到了解了大致原理,有种豁然开朗的感觉。其实分析到现在,只是了解了这个库的核心-对 fragment 的操作。还有一些东西应该也挺有趣的,比如:对 fragment 生命周期的拓展 onSupportVisible
和 onSupportInvisible
、fragment 转场动画的实现、启动模式的处理、debug 模式的任务栈显示等等。之后时间充足的话,可以再分析一下。