从源码角度剖析Fragment核心知识点

1字数 3728阅读 4756

自动Android在3.0版本中退出Fragment以来,fragment在我们日常的开发中无处不在,他使我们的在开发android时能更好的做到view的解耦。关于Fragment的用法,相信大家已经用的滚瓜烂熟了,各种FragmentTransaction的操作,都信手拈来。今天我们要从源码的角度去剖析fragment内部实现的原理,我相信只有了解了内部实现原理,我们在碰到fragment的issue的时候才知道如何去解决。

我们今天要分析的是support v4包中的Fragment,相信绝大部分人都是用的兼容包中的fragment。

先上一张图:

Fragment有关类结构图

这张图描述的是与fragment相关的类的UML图,基本描述了这些类之间的关系。
Fragment理论上可以被任何对象持有,然后管理其生命周期,但是绝大部分时候,我们都是在activity里面使用它,我们可以从Activity出发,理清楚这些类之间的关系。

FragmentActivity

兼容包中,支持fragment的activity叫做FragmentActivity, 我们常用的AppCompatActivity也是继承自它,

在FragmentActivity中有一个重要的成员变量mFragments,它的类型是FragmentController

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

正是这个mFragments的存在使得在FragmentActivity中进行fragment操作成为可能,我们可以看到在FragmentActivity中进行的任何fragment的操作都得经FragmentController之手

 @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);

    super.onCreate(savedInstanceState);

    //...
    mFragments.restoreAllState(p, nc != null ? nc.fragments : null);

    //...
    mFragments.dispatchCreate();
}

public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
}

因此,我们进到FragmentController类中去一探究竟。打开FragmentController,发现其函数实现非常简单。基本上你可以认为FragmentController是FragmentManager的代理,Activity需要的FragmentController的函数,最后调用的都是FragmentHostCallback中的FragmentManager的方法。

FragmentHostCallback,我们可以认为他是用来管理Fragment生命周期,同时作为代理提供fragment需要与外界打交道时的函数实现的比如从fragment中启动新的Activity, 请求权限等等。其Fragment生命周期的的管理由FragmentManager负责,其余部分代理功能由其自己负责。

我们打开FragmentHostCallback的源代码,可以看到其头顶注释中写着:fragment可以被任何对象持有,要使一个对象具有持有和管理fragment生命周期的能力,我们只需要实现FragmentHostCallback中的函数。显然,我们最常见的FragmentActivity肯定实现了FragmentHostCallback,我们跳转到FragmentActivity,看到其内部有一个非静态内部类HostCallbacks, 正式这个内部类的存在,使得FragmentActivity具有了持有和管理Fragment的能力,Fragment与外部交互的功能都由FragmentActivity实现了。至于FragmentController,前面已经说到,只是作为中间桥梁的作用。

Fragment

前面讲了这么多,我们还没有开始介绍今天的主角Fragment, 接下来我们就来揭开其稍许神秘的面纱。

Fragment = Attr + View + + State, 此处等待UML图

Attr

Attr指得是Fragment的一些固有属性,不会随着Fragment的生命周期发生变化的,比如

Bundle mArguments;  //构造参数
boolean mFromLayout; //是否从layout文件中创建
...

View

View是Fragment创建出来并显示给用户的界面的view,如果Fragment被持有,会被添加到Activity某一块layout中去

// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;

// The View generated for this fragment.
View mView;

// The real inner view that will save/restore state.
View mInnerView;

State

State指的是Fragment在生命周期变迁中中会发生改变的状态

int mState = INITIALIZING; //生命周期状态
boolean mAdded; //是否被添加
boolean mRemoving; //是否被移除
boolean mHidden;// 是否被隐藏
boolean mDetached; //是否已经分离
...

其中Fragment的成员变量mState,直接映射了Fragment的生命周期状态变迁,其取值状态在以下常量中:

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

我们在开中经常重写的onCreate, onResume, onStop方法都发生mState的变迁过程中。 接下来,我们就来看看Fragment的生命周期是如何变化的。

Fragment事务操作:BackStackRecord

我们在开发中,每次要操作那个Fragment的添加,删除,隐藏,显示等,都需要使用FragmentTransaction,比如添加一个Fragment:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.contaniner, testFragment);
transaction.commit();

FragmentTransaction实际上是一个抽象类,里面定义了一些关于Fragment操作的函数接口,

public abstract class FragmentTransaction {

    add

    replace

    remove

    hide 

    show

    detach

    attach

    addToBackStack

    ...

}

从FragmentManger.beginTransaction真正返回的确是一个BackStackRecord类, 其实现了FragmentTransaction所定义的接口。

接下来,我们以添加为例,看看BackStackRecord是如何完成Add这个transaction的。

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    fragment.mFragmentManager = mManager;

    if (tag != null) {
        if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
            throw new IllegalStateException("Can't change tag of fragment "
                    + fragment + ": was " + fragment.mTag
                    + " now " + tag);
        }
        fragment.mTag = tag;
    }

    if (containerViewId != 0) {
        if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
            throw new IllegalStateException("Can't change container ID of fragment "
                    + fragment + ": was " + fragment.mFragmentId
                    + " now " + containerViewId);
        }
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }

    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}   

BackStackRecord中实现的四个add函数,最后都调用了doAddOp,我们注意到已经添加过的Fragment其tag和被添加到的containerId是不能更改的,否则会抛异常。 函数最后new了一个Op,Op是啥?Op其实就是Operation的简称,有经验的读者应该知道在一次FragmentTransaction中实际上可以进行多次add,remove之类的操作。每次操作,都会生成一个新的Op对象,在transaction commit操作时,会将这些Operation全部执行掉

我们来看看Op类对象里都有些啥

static final class Op {
    Op next;
    Op prev;
    int cmd;
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;
    ArrayList<Fragment> removed;
}

从next, prev 两个指针可以看出,Op是作为一个链表的节点而存在的,因此FragmentTransaction肯定是在一个链表中存储了一次事务中的所有需要执行的操作。

cmd定义了Operation操作的类型

static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
static final int OP_DETACH = 6;
static final int OP_ATTACH = 7;

fragment指定了这次Operation要操纵哪一个Fragment
此外Op类还包含了转场动画和一个操作会移除的Fragment集合。

doAddOp函数最后执行了

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++;
}   

addOp执行了一个典型的添加节点到链表末尾的数据结构操作,并将transaction的动画赋给op,最后讲mNumOp加一,mNumOp代表了这次transaction中包含的操作个数。如果为0,则isEmpty返回true.

等到操作都添加好了之后,就差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;
}

如果设置了addToBackStack,就会执行添加到返回栈的操作,这个我们后面会专门讲,mManager.enqueueAction 顾名思义就是讲当前的transaction操做入队列,因此在FragmentManager肯定会维护一个队列来存储当前尚未执行的transaction.

public void enqueueAction(Runnable action, boolean allowStateLoss) {
    ...
    synchronized (this) {
        ...
    
        mPendingActions.add(action);
        if (mPendingActions.size() == 1) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

enqueueAction首先会将action,添加到一个叫mPendingAction的列表中去,这个列表中存储着一堆Runnable对象,代表着还未执行的事务,稍等一会儿,我们刚刚说到的是BackStackRecord类对吧,怎么可以添加到Runnable列表中呢,原来BackStackRecod本身还是实现了Runnabled接口,是一个可以执行的对象。添加完毕之后,会调用FragmentHostCallback中提供的getHandler方法,获取到Handler方法,然后向主线程的MessageQueue中发送一个mExecCommit可执行对象

 Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };

public boolean execPendingActions() {

    boolean didSomething = false;

    while (true) {
        int numActions;
        
        synchronized (this) {
            if (mPendingActions == null || mPendingActions.size() == 0) {
                break;
            }
            
            numActions = mPendingActions.size();
            if (mTmpActions == null || mTmpActions.length < numActions) {
                mTmpActions = new Runnable[numActions];
            }
            //讲mPendingAction中的对象转移到mTmpActions
            mPendingActions.toArray(mTmpActions);
            mPendingActions.clear();
            mHost.getHandler().removeCallbacks(mExecCommit);
        }
        
        //遍历mTmpActions,执行run
        mExecutingActions = true;
        for (int i=0; i<numActions; i++) {
            mTmpActions[i].run();
            mTmpActions[i] = null;
        }
        mExecutingActions = false;
        didSomething = true;
    }
    
    ...
    return didSomething;
}

因此绕来绕去,最后调用的BackStackRecord本身的run方法,这也符合了commit方法名字本身的定义,transaction只是被批量提交到了主线程的任务队列里,并不是马上执行,等待主线程的looper去安排这些任务的执行。

那好,我们现在回家吧,去看看BackStackRecord。

public void run() {
    ...
    
    Op op = mHead;
    while (op != null) {
        int enterAnim = state != null ? 0 : op.enterAnim;
        int exitAnim = state != null ? 0 : op.exitAnim;
        switch (op.cmd) {
            case OP_ADD: {
                Fragment f = op.fragment;
                f.mNextAnim = enterAnim;
                mManager.addFragment(f, false);
            } break;
            case OP_REPLACE: {
                  ... 
            } break;
            case OP_REMOVE: {
                Fragment f = op.fragment;
                f.mNextAnim = exitAnim;
                mManager.removeFragment(f, transition, transitionStyle);
            } break;
            case OP_HIDE: {
                ...
            } break;
            ...
            //更多case
        }

        op = op.next;
    }
    //将active状态的fragment全部执行状态变迁检查
    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);

    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    }
}

run方法内部根据不同的cmd走了很不不同的分支,每个分支内部都会对,fragment状态做改变,最后调用moveToState将fragment的生命周期状态mState进行变迁。

当然我们注意到hide和show,它们内部实际上不会调用调用moveToState, 因为hideFragment实际上就做了三件事请,

  • 设置mHidden 为true
  • fragment.mView.setVisibility(View.GONE); 隐藏fragment的view
  • fragment.onHiddenChanged(true); 调用onHiddenChange

showFragment也是类似,只不过行为正好相反。

public void addFragment(Fragment fragment, boolean moveToStateNow) {
    ...
    makeActive(fragment);
    if (!fragment.mDetached) {
        ...
        mAdded.add(fragment);
        fragment.mAdded = true;
        fragment.mRemoving = false;
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment);
        }
    }
}

void makeActive(Fragment f) {
    if (f.mIndex >= 0) {
        return;
    }
    
    if (mAvailIndices == null || mAvailIndices.size() <= 0) {
        if (mActive == null) {
            mActive = new ArrayList<Fragment>();
        }
        f.setIndex(mActive.size(), mParent);
        mActive.add(f);
        
    } else {
        f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
        mActive.set(f.mIndex, f);
    }
    if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}

addFragment会将fragment添加到 mAdded和 mActive这两个集合当中,这两个集合维护了当前activity中维护的已经添加的fragment列表和当前处于活跃状态的fragment列表,如果fragment位于mActive中,那么当activity的状态发生变化时,fragment也会跟随着发生变化。FragmentManger 如何引导fragment的状态发生变化呢?

这一切都发生在moveToState函数当中

Fragment状态变迁:moveToState

Fragment状态变迁发生在用户主动发起transaction,或者fragment被add到activity之后跟随activity的生命周期变化一起发生改变。每次状态变迁最终都会走到函数moveToState,字面意思是将fragment迁移到新的状态

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    // Fragments that are not currently added will sit in the onCreate() state.
    ...
    if (f.mState < newState) {
        // For fragments that are created from a layout, when restoring from
        // state we don't want to allow them to be created until they are
        // being reloaded from the layout.
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                   ...
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                    f.performPause();
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                    f.performStop();
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                   ...
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    ...
                }
        }
    }

    ...
}

fragment的state取值,为前面提到的七中状态,其中最低值是INITIALIZING状态,代表fragment刚创建,还未被add, 最高状态值是RESUMED,代表fragment处于前台。 所以moveToState内部分两条线,状态跃升,和状态降低,里面各有一个switch判断,注意到switch里每个case都没有break,这意味着,状态可以持续变迁,比如从INITIALIZING,一直跃升到RESUMED,将每个case都走一遍,每次case语句内,都会改变state的值。

Fragment状态变迁图

比如我们常见的add操作,最后调用的是

mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);

就是将fragment迁移到FragmentManager当前的状态,因为我们不知道用户什么时候add fragment,因此fragment被add之后,就将其状态迁移到FragmentManager当前的状态,然后跟随FragmentManager一起发生状态变迁,除非用户手动removeFragment将其从mActive列表中移除。

public void dispatchCreate() {
    mStateSaved = false;
    moveToState(Fragment.CREATED, false);
}

public void dispatchActivityCreated() {
    mStateSaved = false;
    moveToState(Fragment.ACTIVITY_CREATED, false);
}

public void dispatchStart() {
    mStateSaved = false;
    moveToState(Fragment.STARTED, false);
}

public void dispatchResume() {
    mStateSaved = false;
    moveToState(Fragment.RESUMED, false);
}

public void dispatchPause() {
    moveToState(Fragment.STARTED, false);
}

public void dispatchStop() {
    // See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    mStateSaved = true;

    moveToState(Fragment.STOPPED, false);
}

public void dispatchReallyStop() {
    moveToState(Fragment.ACTIVITY_CREATED, false);
}

public void dispatchDestroyView() {
    moveToState(Fragment.CREATED, false);
}

public void dispatchDestroy() {
    mDestroyed = true;
    execPendingActions();
    moveToState(Fragment.INITIALIZING, false);
    mHost = null;
    mContainer = null;
    mParent = null;
}

这些 dispatchxxx函数由FragmentActivity状态变化引发,然后调用moveToState将处于mActive集合中的fragment的状态全部变更一次。
比如,当FragmentActivity pause的时候,其会通过FM
通知framgents进行状态变迁。

/**
 * Dispatch onPause() to fragments.
 */
@Override
protected void onPause() {
    super.onPause();
    mResumed = false;
    if (mHandler.hasMessages(MSG_RESUME_PENDING)) {
        mHandler.removeMessages(MSG_RESUME_PENDING);
        onResumeFragments();
    }
    mFragments.dispatchPause();
}

Fragment状态的保存

既然Fragment持有view,以及一些状态属性,那么在Activity保存自身状态以便下次恢复的时候,就需要把fragment的状态也保存起来,这样activity被系统finish掉,然后重新创建时就能恢复上次的fragment状态。那我们首先直奔FragmentActivity的onSaveInstanceState函数:

 /**
 * Save all appropriate fragment state.
 */
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //调用FragmentController 代理FragmentManager 保存状态
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    //保存跟fragment启动activity等待result的状态
    if (mPendingFragmentActivityResults.size() > 0) {
        outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

        int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
        String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
        for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
            requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
            fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
        }
        outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
        outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
    }
}

saveAllState函数就会将FragmentManager在重新恢复fragmentstate时需要的所有信息保存起来,小伙伴们可以跟进去看,我在这里画了张图:

FragmentManager状态的保存

关于返回栈的保存没有详细标注,不过我们后面会再详细讲到。

Fragment 状态的恢复

Fragment状态的恢复其实就是保存状态的逆过程,不过,额外的工作是,我们需要根据之前保存的每个active的fragmentstate来恢复创建Fragment:


public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
    if (mInstance != null) {
        return mInstance;
    }

    final Context context = host.getContext();
    if (mArguments != null) {
        mArguments.setClassLoader(context.getClassLoader());
    }
    //利用保存的状态恢复fragment
    mInstance = Fragment.instantiate(context, mClassName, mArguments);

    if (mSavedFragmentState != null) {
        mSavedFragmentState.setClassLoader(context.getClassLoader());
        mInstance.mSavedFragmentState = mSavedFragmentState;
    }
    mInstance.setIndex(mIndex, parent);
    mInstance.mFromLayout = mFromLayout;
    mInstance.mRestored = true;
    mInstance.mFragmentId = mFragmentId;
    mInstance.mContainerId = mContainerId;
    mInstance.mTag = mTag;
    mInstance.mRetainInstance = mRetainInstance;
    mInstance.mDetached = mDetached;
    mInstance.mFragmentManager = host.mFragmentManager;

    if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
            "Instantiated fragment " + mInstance);

    return mInstance;
}

其中构造Fragment的代码如下:

  public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
    try {
        Class<?> clazz = sClassMap.get(fname);
        if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
        }
        Fragment f = (Fragment)clazz.newInstance();
        if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments = args;
        }
        return f;
    } catch (ClassNotFoundException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (java.lang.InstantiationException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (IllegalAccessException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    }
}

是不是发现这段代码灰常熟悉,很多人恐怕都遇到过代码中描述的异常。因为Fragment在恢复时,是利用反射的方式去创建,首先利用类加载器去加载类,然后调用其pubic empty 构造函数去创建fragment。

所以如果Fragment不是public的,或者fragment没有public的无参构造函数,那么你的应用肯定会碰到这些异常,只不过你在调试的时候可能无法发现,因为你的手机环境太好,构不成saveInstanceState然后恢复它们的条件。

如果我们用Android studio 模板去创建fragment,那么他会给我们默认实现一个newInstance的静态构造函数,并把构造参数写在argument里,因为前面那张图里可以看到,argument会在保存fragment状态时保存起来的。另外不要写任何带参数的构造函数,因为这样子,默认构造函数就会被隐藏,除非你手动去实现它。 话说这个instantiate是个public方法,因此你也可以直接调用这个函数去创建fragment,只不过它使用起来不是那么方便,使用者不知道我们需要往里面传什么参数。

MyFragment.instantiate(activity, MyFragment.getClass().getName(), args)
臭名昭著的 “Can not perform this action after onSaveInstanceState”

这里不得不提一提开发中经常会碰到的一个异常,异常抛出时的message为:“Can not perform this action after onSaveInstanceState”, 意为,FragmentTransaction不能再onSaveInstanceState后提交,为什么会抛出这样一个异常呢,因为FragmentManager认为在onSaveInstanceState 发生之后提交的transaction不能在下次Fm恢复时得到恢复,继而认为这样做是危险的,拒绝提交,除非你指定了allowStateLoss,即允许状态的丢失。

开发者只需要用commitAllowingStateLoss,即可以成功提交这样的transaction,即使它有可能会丢失状态。

Fragment返回栈

使用fragmentTransaction的时候可以将其加入返回栈,这样用户就可以有机会去撤销这一动作,将其从返回栈中pop出来。

public FragmentTransaction addToBackStack(String name) {
    if (!mAllowAddToBackStack) {
        throw new IllegalStateException(
                "This FragmentTransaction is not allowed to be added to the back stack.");
    }
    //标记 即将加入返回栈
    mAddToBackStack = true;
    //标记在返回栈中的名字
    mName = name;
    return this;
}

//transaction提交
public void run() {
    //...

    //加入到FragmentManager的返回栈中
    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    }
}

//FragmentManager添加返回栈
 void addBackStackState(BackStackRecord state) {
    if (mBackStack == null) {
        mBackStack = new ArrayList<BackStackRecord>();
    }
    mBackStack.add(state);
    //通知返回栈监听者们发生改变
    reportBackStackChanged();
}
    

FragmentManager中有一个叫做mBackStack的列表,保存了添加到返回栈的所有record, 当用户选择popBackStack的时候,就可以将其pop出来。

我们常用的pop操作就是popBackStack函数,其实FM还提供了很多pop操作,它可以指定以name和id指定pop哪个record,还可以指定flag:POP_BACK_STACK_INCLUSIVE, 如果指定了这个flag就表示将name或者id相等且连续的record全部pop出来。这些操作最后都调用了函数

boolean popBackStackState(Handler handler, String name, int id, int flags) {
    //如果返回栈不存在,立即返回
    if (mBackStack == null) {
        return false;
    }
    //如果name、id、POP_BACK_STACK_INCLUSIVE全部都没有设置,直接移除栈顶的一个record
    if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
        int last = mBackStack.size()-1;
        if (last < 0) {
            return false;
        }
        final BackStackRecord bss = mBackStack.remove(last);
        SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
        SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
        bss.calculateBackFragments(firstOutFragments, lastInFragments);
        bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
        reportBackStackChanged();
    } else {
        int index = -1;
        if (name != null || id >= 0) {
            //从栈顶开始寻找一个name或者id匹配的record,找到即跳出循环
            index = mBackStack.size()-1;
            while (index >= 0) {
                BackStackRecord bss = mBackStack.get(index);
                if (name != null && name.equals(bss.getName())) {
                    break;
                }
                if (id >= 0 && id == bss.mIndex) {
                    break;
                }
                index--;
            }
            //如果没找到,函数返回
            if (index < 0) {
                return false;
            }
            //如果设置了POP_BACK_STACK_INCLUSIVE,则向栈底方向寻找连续匹配的record
            if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
                index--;
                // Consume all following entries that match.
                while (index >= 0) {
                    BackStackRecord bss = mBackStack.get(index);
                    if ((name != null && name.equals(bss.getName()))
                            || (id >= 0 && id == bss.mIndex)) {
                        index--;
                        continue;
                    }
                    break;
                }
            }
        }
        if (index == mBackStack.size()-1) {
            return false;
        }
        //弹出从栈顶到连续相同匹配的record记录位置的所有record,加入集合states中
        final ArrayList<BackStackRecord> states
                = new ArrayList<BackStackRecord>();
        for (int i=mBackStack.size()-1; i>index; i--) {
            states.add(mBackStack.remove(i));
        }
     
        final int LAST = states.size()-1;
        SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
        SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
        //计算在连续record中,最先remove的fragment集合 和 最后进入的fragment集合
        for (int i=0; i<=LAST; i++) {
            states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
        }
        BackStackRecord.TransitionState state = null;
        //对这些record列表执行逆操作
        for (int i=0; i<=LAST; i++) {
            if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
            state = states.get(i).popFromBackStack(i == LAST, state,
                    firstOutFragments, lastInFragments);
        }
        //通知返回栈监听器更新
        reportBackStackChanged();
    }
    return true;
}

其中比较复杂的两步,一步是calculateBackFragments, 还有一个是popFromBackStack,我们先看calculateBackFragments:

为什么需要计算calculateBackFragments? 其实是因为涉及到动画的方面,在一次在一次transaction中可能会移除和添加多次fragments,这样动画的编排就必须按顺序来,这次分析中,我们就不对动画做过多分析了,因为这是相对来说次要点的知识点。所以我们集中关心BackStackRecord 的popFromBackStack函数

public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
            SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
    
    //...动画相关


    int transitionStyle = state != null ? 0 : mTransitionStyle;
    int transition = state != null ? 0 : mTransition;
    Op op = mTail;
    while (op != null) {
        int popEnterAnim = state != null ? 0 : op.popEnterAnim;
        int popExitAnim= state != null ? 0 : op.popExitAnim;
        switch (op.cmd) {
            case OP_ADD: {
                Fragment f = op.fragment;
                f.mNextAnim = popExitAnim;
                mManager.removeFragment(f,
                        FragmentManagerImpl.reverseTransit(transition), transitionStyle);
            } break;
            case OP_REPLACE: {
                Fragment f = op.fragment;
                if (f != null) {
                    f.mNextAnim = popExitAnim;
                    mManager.removeFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                }
                if (op.removed != null) {
                    for (int i=0; i<op.removed.size(); i++) {
                        Fragment old = op.removed.get(i);
                        old.mNextAnim = popEnterAnim;
                        mManager.addFragment(old, false);
                    }
                }
            } break;
            case OP_REMOVE: {
                Fragment f = op.fragment;
                f.mNextAnim = popEnterAnim;
                mManager.addFragment(f, false);
            } break;
            case OP_HIDE: {
                Fragment f = op.fragment;
                f.mNextAnim = popEnterAnim;
                mManager.showFragment(f,
                        FragmentManagerImpl.reverseTransit(transition), transitionStyle);
            } break;
            case OP_SHOW: {
                Fragment f = op.fragment;
                f.mNextAnim = popExitAnim;
                mManager.hideFragment(f,
                        FragmentManagerImpl.reverseTransit(transition), transitionStyle);
            } break;
            case OP_DETACH: {
                Fragment f = op.fragment;
                f.mNextAnim = popEnterAnim;
                mManager.attachFragment(f,
                        FragmentManagerImpl.reverseTransit(transition), transitionStyle);
            } break;
            case OP_ATTACH: {
                Fragment f = op.fragment;
                f.mNextAnim = popEnterAnim;
                mManager.detachFragment(f,
                        FragmentManagerImpl.reverseTransit(transition), transitionStyle);
            } break;
            default: {
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
        }

        op = op.prev;
    }

    if (doStateMove) {
        mManager.moveToState(mManager.mCurState,
                FragmentManagerImpl.reverseTransit(transition), transitionStyle, true);
        state = null;
    }

    if (mIndex >= 0) {
        mManager.freeBackStackIndex(mIndex);
        mIndex = -1;
    }
    return state;
}

基本上从多个switch-case分支语句里就可以看出pop执行的就是commit的反操作,因为因为transaction都是成对存在的

  • add <-> remove
  • attach <-> detach
  • show <-> hide
  • replace( remove x n + add x m) <-> replace ( remove x m + add x n)

返回栈的状态的保存

返回栈状态的保存,相当于要把整个BackStackRecord的列表保存下来,以便下次恢复,前面提到BackStackRecord,可以跟 BackStackState这个parcelable映射起来。基本上BackStackRecord都是可以parcel的类型,出了Op链表,Op链表映射为BackStackState的int[] mOps;

看完了返回栈,基本上有关Fragment的核心知识点都了解的差不多了,当然我们也跳过了一些知识点,比如有关动画的部分、ChildFragmentManager等。但基本上Fragment在使用到遇到问题的部分,都可以从上面的分析中找到原因。

推荐阅读更多精彩内容