从源码角度看 Fragment

简介

Android系统所运行的设备中,从小屏手机到11寸平板再到超大尺寸的电视,五花八门层出不穷。一个应用如何适配这些尺寸不同的设备呢,如果只是单纯的为每个APP开发一套应用界面的话,其中的工作量将会呈指数被增长。于是乎,Fragment碎片化的概念就这样被推出了,Fragment运行在Activity中,拥有着和Activity对应的生命周期,同样能够显示与响应界面和用户的点击事件。Fragment之间是相互独立了,在Activity中,可以自由增加与删除被管理的Fragment。通过Fragment,一个APP可以做到同时适应手机、平板和电视这些尺寸不同的设备

本篇文章里,我将按惯例先放出Fragment涉及到的类图,方便大家快速熟悉主要的类与其中关系。随后我将着重讲解Activity与Fragment生命周期的调用顺序并且给出一张详细的图片用来参考,之后我会着重分析Fragment事务的实现及其原理。文章的最后我会给出简短的总结

这篇文章主要重点放在Fragment的生命周期与事务操作的流程与源码分析,具体细节、坑点与调用技巧没有做详细讨论

类图

查看大图

  • AMS控制Activity的生命周期,Activity在dispatchXXX系列方法中通过调用FragmentController来控制Fragment的生命周期
  • FragmentController是一个代理,本身没有实现重要的逻辑
  • FragmentHostCallback被FragmentController持有,引用了FragmentManager的具体实现类对象
  • FragmentManagerImpl是FragmentManager的具体实现,负责控制Fragment列表的状态,是真正做操作的一个类
  • BackStackRecord是FragmentTransaction的实现,用来实现Fragment事务的操作

Actiity与Fragment的生命周期

知道启动Activity流程的同学应该都清楚,从调用startActivity开始到应用调用onCreate为止,这期间的操作不仅仅只需要应用自身去完成,同时还需要AMS以服务端的角色参与其中,Activity的生命周期和AMS中的ActivitySupervisor所记录的状态是严格对应的,如果双方任何一端出现了异常,那么很有可能应用就会出现Crash

同理,Fragment的生命周期也是和Activity强关联一一对应的。稍微形象一点的说法,Activity就好比Fragment的服务端,Fragment的生命周期变化都是通过Activity的调用来改变的。当然,Fragment对于Activity生命周期变化的反应是不需要应用开发者自己去处理的,这些控制代码都写在了Activity生命周期的调用函数中了

有兴趣的同学可以搜索Activity的方法performXXX系列,这些方法都是默认package并且是final的,不允许被修改

以下是我自己画的一张Activity与Fragment在正常启动随后正常退出的生命周期图,与官方图不同的是,这张图明确标明了Activity与Fragment执行生命周期方法的顺序,这其中只有Activity的onCreate方法特殊,在onCreate方法内执行了Fragment的4个方法。其余的Activity方法都是在Fragment生命周期方法之前或之后执行完的

状态切换的具体方法是通过FragmentManager.moveToState来实现的,具体的实现源码最后再进行分析

查看大图

  • Activity的各个重要生命周期方法都能够在Fragment找到
  • Fragment最先与最后调用的方法分别是onAttach与onDetach方法
  • 正常打开Activity时,Activity的生命周期方法先于对应的Fragment生命周期方法被调用
  • 正常关闭Activity时,Activity的生命周期方法在对应的Fragment生命周期方法之后被调用

Fragment事务

以下是在Activity通过调用getFragmentManager获取FragmentManager对象来进行fragment管理的操作,通过调用FragmentTransation的add操作将VideoFragment的实例加入到当前Activity中

  • Fragment videoFragment = new VideoPlayerFragment();
  • FragmentTransaction transaction = getFragmentManager().beginTransaction();
  • transaction.add(R.id.video_fragment, videoFragment).commit();

beginTransaction与BackStackRecord

[frameworks/base/core/java/android/app/FragmentHostCallback.java]

  • FragmentManagerImpl getFragmentManagerImpl() {
  • return mFragmentManager;
  • }

通过Activity.getFragementManager层层调用,最终返回了FragmentManagerImpl实例,这之前的传递关系可以在类图中很清晰的看到

[frameworks/base/core/java/android/app/FragmentManager.java]

  • @Override
  • public FragmentTransaction beginTransaction() {
  • // 直接返回BackStackRecord的实例,这个实例实现了FragementTransaction抽象类
  • return new BackStackRecord(this);
  • }

BackStackRecord是FragmentTransaction的实现,主要维护了一个双向的链表,用以实现事务的前进与回退

[frameworks/base/core/java/android/app/BackStackRecord.java]

  • public FragmentTransaction add(Fragment fragment, String tag) {
  • doAddOp(0, fragment, tag, OP_ADD);
  • return this;
  • }

这个add操作调用的方法和其他操作其实都一样,最终会调用到addOp方法,唯一的差别在于command不同

事务操作

操作 command 意义
add OP_ADD 添加fragment
remove OP_REMOVE 删除fragment
replace OP_REPLACE 替换fragment
show OP_SHOW 显示fragment
hide OP_HIDE 隐藏fragment
attach OP_ATTACH 依附activity
detach OP_DETACH 从activity分离

commit

[frameworks/base/core/java/android/app/BackStackRecord.java]

  • public int commit() {
  • return commitInternal(false);
  • }
  • int commitInternal(boolean allowStateLoss) {
  • mManager.enqueueAction(this, allowStateLoss);
  • return mIndex;
  • }

调用commit方法后,BackStackRecord没有再做过多的操作,直接交给了FragmentManager进行下一步处理

[frameworks/base/core/java/android/app/FragmentManager.java]

  • public void enqueueAction(Runnable action, boolean allowStateLoss) {
  • synchronized (this) {
  • // 将commit处理操作放置到mPendingActions列表中
  • mPendingActions.add(action);
  • // 如果mPendingActions的数量唯一,则将commit执行runnable放置到主线程Looper中
  • if (mPendingActions.size() == 1) {
  • mHost.getHandler().removeCallbacks(mExecCommit);
  • mHost.getHandler().post(mExecCommit);
  • }
  • }
  • }

[frameworks/base/core/java/android/app/FragmentManager.java]

  • Runnable mExecCommit = new Runnable() {
  • @Override
  • public void run() {
  • execPendingActions();
  • }
  • };
  • public boolean execPendingActions() {
  • boolean didSomething = false;
  • // 进入死循环
  • while (true) {
  • int numActions;
  • synchronized (this) {
  • // 如果当前mPendingActions没有任务时,就会终止循环
  • if (mPendingActions == null || mPendingActions.size() == 0) {
  • break;
  • }
  • numActions = mPendingActions.size();
  • if (mTmpActions == null || mTmpActions.length < numActions) {
  • mTmpActions = new Runnable[numActions];
  • }
  • // 将mPendingActions列表的action拷贝到mTempActions数组里
  • mPendingActions.toArray(mTmpActions);
  • 随后清理mPendingActions列表
  • mPendingActions.clear();
  • mHost.getHandler().removeCallbacks(mExecCommit);
  • }
  • mExecutingActions = true;
  • for (int i=0; i<numActions; i++) {
  • // 在主线程执行BackStackRecord的run方法
  • mTmpActions[i].run();
  • mTmpActions[i] = null;
  • }
  • mExecutingActions = false;
  • didSomething = true;
  • }
  • return didSomething;
  • }

FragmentManager继续commit操作之后,随后会将消息post到主线程队列中,然后挨个执行BackStackRecord的run方法

BackStackRecord.run (1)

[frameworks/base/core/java/android/app/BackStackRecord.java]

  • public void run() {
  • SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
  • SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
  • // 计算出首先被remove的Fragment以及最后被add的Fragment;array维护了一个containerId与fragment对应的map
  • calculateFragments(firstOutFragments, lastInFragments);
  • // 计算完成后,会根据exit/shared element/enter这三种状态来进行切换
  • // 这块不具体分析了
  • beginTransition(firstOutFragments, lastInFragments, false);
  • }
  • private void calculateFragments(SparseArray<Fragment> firstOutFragments,
  • SparseArray<Fragment> lastInFragments) {
  • Op op = mHead;
  • while (op != null) {
  • switch (op.cmd) {
  • case OP_ADD:
  • setLastIn(lastInFragments, op.fragment);
  • break;
  • case OP_REPLACE: {
  • Fragment f = op.fragment;
  • if (mManager.mAdded != null) {
  • for (int i = 0; i < mManager.mAdded.size(); i++) {
  • Fragment old = mManager.mAdded.get(i);
  • if (f == null || old.mContainerId == f.mContainerId) {
  • if (old == f) {
  • f = null;
  • } else {
  • setFirstOut(firstOutFragments, old);
  • }
  • }
  • }
  • }
  • setLastIn(lastInFragments, f);
  • break;
  • }
  • case OP_REMOVE:
  • setFirstOut(firstOutFragments, op.fragment);
  • break;
  • case OP_HIDE:
  • setFirstOut(firstOutFragments, op.fragment);
  • break;
  • case OP_SHOW:
  • setLastIn(lastInFragments, op.fragment);
  • break;
  • case OP_DETACH:
  • setFirstOut(firstOutFragments, op.fragment);
  • break;
  • case OP_ATTACH:
  • setLastIn(lastInFragments, op.fragment);
  • break;
  • }
  • op = op.next;
  • }
  • }

commit的第一步操作很简单,遍历所有的Op对象,根据cmd规则计算出firstOutFragment与lastInFragment,containerId不同的话会产生多条数据;以下是上面代码的具体规则

cmd firstOutFragments lastInFragements
OP_ADD - 被添加的fragment
OP_REPLACE 被替换的fragment 新的fragment
OP_REMOVE 被删除的fragment -
OP_HIDE 被隐藏的fragment -
OP_SHOW - 被显示的fragment
OP_DETACH 被detach的fragment -
OP_ATTACH - 被attach的fragment

BackStackRecord.run (2)

我们可以先看看Op类的具体实现,很明显的可以看出这是一个支持前进与回退的双向链表

[frameworks/base/core/java/android/app/BackStackRecord.java]

  • final class BackStackRecord extends FragmentTransaction implements
  • FragmentManager.BackStackEntry, Runnable {
  • static final class Op {
  • // 双向的链表,维护着next和prev两个引用
  • Op next;
  • Op prev;
  • // 具体命令
  • int cmd;
  • // 被操作的fragment
  • Fragment fragment;
  • // 进入退出的动画
  • int enterAnim;
  • int exitAnim;
  • // 弹出弹入的动画
  • int popEnterAnim;
  • int popExitAnim;
  • ArrayList<Fragment> removed;
  • }
  • // 引用着头与尾
  • Op mHead;
  • Op mTail;
  • int mNumOp;
  • int mEnterAnim;
  • int mExitAnim;
  • int mPopEnterAnim;
  • int mPopExitAnim;

[frameworks/base/core/java/android/app/BackStackRecord.java]

```java
public void run() {
Op op = mHead;
// 遍历Op双向链表
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 = 0; i < mManager.mAdded.size(); i++) { // 寻找需要被替换的fragment 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();
}
op.removed.add(old);
old.mNextAnim = op.exitAnim;
// 找到后进行移除
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
}
}
}
// 将新的fragment进行add
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: {

top Created with Sketch.