先普及一个知识
当你调用了碎片空的构造器的时候,你的碎片并不会执行生命周期的方法,如onCreateView()等
什么时候会执行生命周期呢?
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(mContainerId, mFragment);
transaction.commit();
当你把这个碎片add进fragmentmanager(碎片栈),或者replace(是remove和add的结合体)进去的时候,你才会进入碎片的生命周期。
既然commit就可以进入碎片的生命周期那commitNow又是做什么用的?
顾名思义,commitNow就是立刻提交事务,那也不难推断出commit其实提交后,并没有立刻执行你所期待的操作。
那么什么时候执行呢?他们的具体区别到底在哪呢?我用一个实例分析一下。
NavFragment navFragment = new NavFragment(); addFragment(R.id.fl_nav, navFragment); navFragment.setup();如果你的addFragment里的事务提交是commit方法,那么你的执行流程是这样的:虽然你addFragment方法里你commit了,但是commit是会被搁置的,当你活动中接下来的所有初始化代码执行完以后,才会去真正执行把碎片add进fragmentmanager的栈中,并且执行碎片一系列的生命周期操作。
如果你的addFragment里的事务提交是commitNow方法,那你的执行流程是这样的:先把碎片那一系列的生命周期操作执行了,让你的碎片真正被“激活”了,才会按顺序执行你activity中的余下代码。
我们先追溯一下commit的源码,在FragmentTransaction中
/** * Schedules a commit of this transaction. The commit does * not happen immediately; it will be scheduled as work on the main thread * to be done the next time that thread is ready. * * <p class="note">A transaction can only be committed with this method * prior to its containing activity saving its state. If the commit is * attempted after that point, an exception will be thrown. This is * because the state after the commit can be lost if the activity needs to * be restored from its state. See {@link #commitAllowingStateLoss()} for * situations where it may be okay to lose the commit.</p> * * @return Returns the identifier of this transaction's back stack entry, * if {@link #addToBackStack(String)} had been called. Otherwise, returns * a negative number. */ public abstract int commit();
你会惊讶的发现commit是一个抽象方法,并且附上了一段密密麻麻的注释。先不急找到这个方法的实现类,我们采用谷歌翻译对注释进行翻译抽取有用的信息。
计划提交此事务。提交确实不是马上发生;它将被安排在主线程上工作,待下次线程准备完成。
后面是讲到如果是因为你活动需要执行了这个commit,那么你在排队的过程中信息有可能会丢失,但是可以从存储的状态中恢复(没啥用)
经过寻找,BackStackRecord是FragmenTransaction的实现类。我们定位commit的方法
@Override public int commit() { return commitInternal(false); }
继续寻找commitInternal方法
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; }上面的debug不用看,看一下if语句里的,mIndex = mManager.allocBackStackIndex(this);意思就是取得当前回退栈中的序列号返回,其实也不用多关注,我们需要关注的是mManager.enqueueAction(this, allowStateLoss);方法。
我们再度定位一下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) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<>(); } mPendingActions.add(action); scheduleCommit(); } }看一下传进来的第一个参数,有些人可能很好奇,我们传的是this,就是把BackStackRecord的实例传进来了,怎么回是这样一个对象?难道BackStackRecord实现了这个接口?
final FragmentManagerImpl mManager;
并没有,在那里的上下文是这个mManager,可以倒回去看一下,这个FragmentManager的实现类实现了OpGenerater这个接口。那就是把我们当前的操作传了进来(比如add碎片进栈,remove出栈,detach解除联系之类的操作)
ArrayList<OpGenerator> mPendingActions;
mPendingActions.add(action); scheduleCommit();这里第一句话就是把这个action进入等待序列中,其实就是用一个arrayList把操作存进去,等待执行。
然后下一行就是规划这个action的执行时间了。
继续追溯scheduleCommit()
/** * Schedules the execution when one hasn't been scheduled already. This should happen * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when * a postponed transaction has been started with * {@link Fragment#startPostponedEnterTransition()} */ 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); } } }Handler可以用来更新UI,也可以用来发送消息、处理消息。
道理我们都懂,那么mExecCommit的具体代码又是怎样的?
Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } };这就很清晰了,开了一个子线程来执行等待队列里的操作。我们就是传一个子线程的实现给handler的post方法。
追溯post方法,这里就进入到我们熟悉的领域了
/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }所以我们线程在这里被执行
追溯sendMessageDelayed
/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
然后就把东西放到消息队列里,在设定的时间间隔后开始执行。不讲了,又是一系列出入队列的操作。
下面看看commitNow的源码,明白和commit的区别
翻译:同步执行这个事务,就是立刻执行,所有被加入的碎片都会被立刻完成生命周期状态,所以移除的碎片都会被撕碎。* Commits this transaction synchronously. Any added fragments will be * initialized and brought completely to the lifecycle state of their host * and any removed fragments will be torn down accordingly before this * call returns
* <p>Transactions committed in this way may not be added to the * FragmentManager's back stack, as doing so would break other expected * ordering guarantees for other asynchronously committed transactions.翻译:以这种方式提交的交易可能不会被添加到FragmentManager的回退栈,这样做会破坏其他想要异步提交的事务(指代的就是commit,异步提交的事务)
追溯commitNow
@Override public void commitNow() { disallowAddToBackStack(); mManager.execSingleAction(this, false); }disallowAddToBackStack();佐证了观点上面翻译里的观点,不允许添加到回退栈中。
所以我们要执行execSingleAction,开始主线程里跑这个事务提交了
追溯execSingleAction
public void execSingleAction(OpGenerator action, boolean allowStateLoss) { ensureExecReady(allowStateLoss); if (action.generateOps(mTmpRecords, mTmpIsPop)) { mExecutingActions = true; try { optimizeAndExecuteOps(mTmpRecords, mTmpIsPop); } finally { cleanupExec(); } } doPendingDeferredStart(); }
一开始我被误导了,以为optimizeAndExecuteOps才是执行commitNow的方法,其实这个方法是进行commitNow完成后的扫尾操作。因为commitNow直接在主线程提交的事务,所以是一种线程不安全的操作,并且影响了其他的transaction,所以后面的都是对其进行扫尾和优化的工作。
真正的执行
action.generateOps(mTmpRecords, mTmpIsPop)
所以是在这个接口的实现类里跑的。
/** * An add or pop transaction to be scheduled for the UI thread. */ interface OpGenerator { /** * Generate transactions to add to {@code records} and whether or not the transaction is * an add or pop to {@code isRecordPop}. * * records and isRecordPop must be added equally so that each transaction in records * matches the boolean for whether or not it is a pop in isRecordPop. * * @param records A list to add transactions to. * @param isRecordPop A list to add whether or not the transactions added to records is * a pop transaction. * @return true if something was added or false otherwise. */ boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop); }
具体实现是在哪呢?我也不知道啊,继承还能找找,这个我上哪去找啊。反正就是在这里执行的commitNow是跑不了的。
最后我找到了这个接口的实现,是在BackStackRecord中重写的。
/** * Implementation of {@link FragmentManagerImpl.OpGenerator}. * This operation is added to the list of pending actions during {@link #commit()}, and * will be executed on the UI thread to run this FragmentTransaction. * * @param records Modified to add this BackStackRecord * @param isRecordPop Modified to add a false (this isn't a pop) * @return true always because the records and isRecordPop will always be changed */ @Override public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) { if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "Run: " + this); } records.add(this); isRecordPop.add(false); if (mAddToBackStack) { mManager.addBackStackState(this); } return true; }