美文网首页
ragment切换时,transaction调用commit()

ragment切换时,transaction调用commit()

作者: helang1991 | 来源:发表于2019-05-24 15:55 被阅读0次

    问题

    最近遇到个问题,当多个fragment被添加到activity中后,如果在当前fragment中停留过久的话,再去操作fragment之间的切换,可能会造成ANR(PS.这个应用是运行在一种配置很低的设备上)。

    思索

    当时就想,是不是由于Android系统在资源吃紧的情况下,会自动回收后台的进程,但是,只有一个activity+多个fragment,对所有的生命周期进行了日志观察,并未有任何异常;然后,我又对内存dump文件进行了分析,所有关键的对象都没有被回收;当我对调用的函数进行trace分析后,发现在执行fragmentTransaction.commit()的时候,出现了“卡死”,也可以认为是阻塞了

    fragmentTransaction.commit()

    既然在这里出现阻塞,那就看看里面到底是什么

    /**
         * 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()里面到底是如何实现的。

    在BackStackState.java中,找到了commit()的具体实现

    @Override
    public int commit() {
         return commitInternal(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);//调用了Fragment中的方法
            return mIndex;
        }
    

    关键函数是FragmentManager中的enqueueAction()方法,这个方法是将trasaction.commit()等这样的动作Action加到队列中

    /**
         * 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);//将commit()这样的一个action添加到延迟动作队列中
                scheduleCommit();//提交action,并不会立即执行,需要按照action的先后顺序来执行
            }
        }
    

    scheduleCommit()后面调用的一系列函数,大致意思就是要在主线程中按照队列的先后来执行action,所以当前需要执行action提交后,如果mPendingActions前面还有其他的action未执行完,那当前的action就会被卡住,最后,可能就出现了ANR。

    解决方案

    既然设计了队列,也应该可以插队了。于是就找到了commitNow()

    /**
         * 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. Committing a transaction in this way allows fragments
         * to be added as dedicated, encapsulated components that monitor the
         * lifecycle state of their host while providing firmer ordering guarantees
         * around when those fragments are fully initialized and ready. Fragments
         * that manage views will have those views created and attached.
         *
         * <p>Calling <code>commitNow</code> is preferable to calling
         * {@link #commit()} followed by {@link FragmentManager#executePendingTransactions()}
         * as the latter will have the side effect of attempting to commit <em>all</em>
         * currently pending transactions whether that is the desired behavior
         * or not.</p>
         *
         * <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.
         * This method will throw {@link IllegalStateException} if the transaction
         * previously requested to be added to the back stack with
         * {@link #addToBackStack(String)}.</p>
         *
         * <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>
         */
        public abstract void commitNow();
    

    commitNow字面意思就是立即提交,但是注释中也警告了我们,可能会失去fragment的state,根据我当前的业务来看,不需要保存fragment的state,因此,就可以使用该方法。

    作者:何浪
    来源:CSDN
    原文:https://blog.csdn.net/helang296479893/article/details/90515970
    版权声明:本文为博主原创文章,转载请附上博文链接!

    相关文章

      网友评论

          本文标题:ragment切换时,transaction调用commit()

          本文链接:https://www.haomeiwen.com/subject/nhxnzqtx.html