从源码角度看 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.