Android碎片事务提交transaction.commit()和transaction.commitnow()的区别以及源码完全解析

来源:互联网 发布:画设计图用什么软件 编辑:程序博客网 时间:2024/06/05 16:11

先普及一个知识

当你调用了碎片空的构造器的时候,你的碎片并不会执行生命周期的方法,如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的方法

@Overridepublic 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

@Overridepublic 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 */@Overridepublic 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;}

原创粉丝点击