使用Fragment的时候,我们要使用两个类:FragmentManger和FragmentTransaction。我们通过FragmentManger提供的方法可以获取到FragmentTransaction对象。
第一步:FragmentManger 是一个抽象类
在Activity中,我们通过使用getSupportFragmentManger()方法获取该对象的实例。接下来我们使用beginTranscation()方法获取一个FragmentTransaction对象。但是因为FragmentManger是一个抽象类,没有方法的实现,他的内部提供了一个实现的子类为FragmentManagerImpl。然后看下实现的beginTranscation()方法如下:
@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
创建一个BackStackRecord()对象。那这个对象是什么鬼,然后发现原来FramentTransaction也是个抽象的类,其子类是BackStackRecord类。也就是说通过beginTranscation()方法我们就获取到了FragmentTransaction实现的子类。
第二步:添加Fragment
获取到FragmentTransaction实例后,我们就使用了add()方法进行添加Fragment对象。如下:
getSupportFragmentManager() .beginTransaction() .add(R.id.fragment_content, mCurrentFragment) .commitAllowingStateLoss();
看下add()内部的代码:
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);
然后进入到子类中看实现的代码:
@Override public FragmentTransaction add(int containerViewId, Fragment fragment) { doAddOp(containerViewId, fragment, null, OP_ADD); return this; }
然后进入到doAddOp()方法中看下:
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { final Class fragmentClass = fragment.getClass(); final int modifiers = fragmentClass.getModifiers(); if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers) || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) { throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName() + " must be a public static class to be properly recreated from" + " instance state."); } fragment.mFragmentManager = mManager; //设置的framngent对应的FragmentManger 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 (containerViewId == View.NO_ID) { // Used to mark a View that has no ID. throw new IllegalArgumentException("Can't add fragment " + fragment + " with tag " + tag + " to container view with no id"); } 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; } addOp(new Op(opcmd, fragment)); }
其中最后一个参数opcmd代表的是标志位,在内部还是几个如下:
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; static final int OP_SET_PRIMARY_NAV = 8; static final int OP_UNSET_PRIMARY_NAV = 9;
然后进入到addop()方法中看下:
void addOp(Op op) { mOps.add(op); op.enterAnim = mEnterAnim; op.exitAnim = mExitAnim; op.popEnterAnim = mPopEnterAnim; op.popExitAnim = mPopExitAnim; }
其中mOps是一个列表,这个方法就将保存了Fragment的op对象放入到列表中:
ArrayList<Op> mOps = new ArrayList<>();
然后看下Op这个对象:
static final class Op { int cmd; Fragment fragment; int enterAnim; int exitAnim; int popEnterAnim; int popExitAnim; Op() { } Op(int cmd, Fragment fragment) { this.cmd = cmd; this.fragment = fragment; } }
第三步:commit()提交任务
依然进入到FragmentTranscation类查看该方法如下:
public abstract int commit(); //提交一个事务
然后进入到真正实现的BackStackRecord类中查看该方法的实现,如下:
@Override public int commit() { return commitInternal(false); }
注意这个方法他传入了一个false,这个false很重要,他代表的是什么意思呢?继续向下看:
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); pw.close(); } mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }
这个方法将mCommitted设置为true,也就是说已经提交了。mAddToBackStack是判断是否可以添加,在初始化的时候设置为true.
进入到allocBackStatckIndex()方法看下:
public int allocBackStackIndex(BackStackRecord bse) { synchronized (this) { if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { if (mBackStackIndices == null) { mBackStackIndices = new ArrayList<BackStackRecord>(); } int index = mBackStackIndices.size(); if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); mBackStackIndices.add(bse); return index; } else { int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); mBackStackIndices.set(index, bse); return index; } } }
这个方法中,我们将FragmentTranscation的子类BackStackRecord添加到了一个存放的队列中。
然后现在我们传递过来的allowStateLoss = false,然后我们进入到enqueueAction()方法中看看做了些什么?
/** * Adds an action to the queue of pending actions. * * @param action the action to add * @param allowStateLoss whether to allow loss of state information * @throws IllegalStateException if the activity has been destroyed */ public void enqueueAction(OpGenerator action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { if (allowStateLoss) { // This FragmentManager isn't attached, so drop the entire transaction. return; } throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<>(); } mPendingActions.add(action); scheduleCommit(); } }
这个方法的目的就是添加活动到列表中,然后进行一次检测。我们之前传allowStateLoss为false,首先会进入到checkStateLoss()方法中,进入看看这个方法都做了些什么?
private void checkStateLoss() { if (isStateSaved()) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } }
只是进行了一个isStateSaved的判断处理,如果isStateSaved()返回的是true的话,就报异常。我们看看这个方法里面,如下:
@Override public boolean isStateSaved() { // See saveAllState() for the explanation of this. We do this for // all platform versions, to keep our behavior more consistent between // them. return mStateSaved || mStopped; }
非常的简单,就是判断mStateSaved和mStopped有一个为真就返回true。然后,我们看看mStateSaved是在哪里进行设置的?几个方法中,我发现基本都是将mStateSaved设置成false的。那什么地方设置成了true呢,这样才会报crash。然后在这个方法中,我们发现了设置:
Parcelable saveAllState() { // Make sure all pending operations have now been executed to get // our state update-to-date. mStateSaved = true; mSavedNonConfig = null; if (mActive == null || mActive.size() <= 0) { return null; }
哪是哪里调用的这个方法呢?继续寻找,然后发现在Actvitiy的onSaveInstanceState()方法中调用了这个方法,如下:
/** * Save all appropriate fragment state. */ @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); markState(getSupportFragmentManager(), Lifecycle.State.CREATED); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } }也就是说,当activity调用了onSaveInstance()方法后,我们在此进行checkStateLoss()的方法时,就会报异常。
然后在回到 enqueueAction()方法中,现在看下scheduleCommit()方法,做了些什么工作,如下:
private void scheduleCommit() { synchronized (this) { boolean postponeReady = mPostponedTransactions != null && !mPostponedTransactions.isEmpty(); boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1; if (postponeReady || pendingReady) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); } } }
这个方法是获取了activity的handler,然后切换到主线程中执行了mExecCommit任务,我们看看做了些什么?
Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } };
进行进入到方法中看下:
/** * Only call from main thread! */ public boolean execPendingActions() { ensureExecReady(true); boolean didSomething = false; while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) { mExecutingActions = true; try { removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); } finally { cleanupExec(); } didSomething = true; } doPendingDeferredStart(); burpActive(); return didSomething; }
大概的意思也就是应该通知activity刷新ui。
第四步:使用show展示
首先进入到FragmentTransaction看下这个方法,如下:
/** * Shows a previously hidden fragment. This is only relevant for fragments whose * views have been added to a container, as this will cause the view to * be shown. * * @param fragment The fragment to be shown. * * @return Returns the same FragmentTransaction instance. */ public abstract FragmentTransaction show(Fragment fragment);
然后进入到BackStackRecord看下子类的实现,如下:
@Override public FragmentTransaction show(Fragment fragment) { addOp(new Op(OP_SHOW, fragment)); return this; }
非常简单的通过addOp()。然后在使用commit()进行提交,走相同的步骤。
同时如果我们不希望进行checkStateLoss()的检测工作。我们可以使用commitAllowingStateLoss()方法,看看他和commit的区别
@Override public int commit() { return commitInternal(false); } @Override public int commitAllowingStateLoss() { return commitInternal(true); }
就是进入到commitInternal()方法中传入的值是不同的,这个参数就是决定是否要走checkStateLoss()这个方法,是否在执行完onSaveInstatncState()方法后,再执行其他show()等方法的时候,是否要报异常。