美文网首页
【源码分析】FragmentTransaction.replac

【源码分析】FragmentTransaction.replac

作者: 喵喵好运 | 来源:发表于2021-01-23 02:42 被阅读0次

    知道如果写应用首页,我们会以主Activity+多个Fragment的方式去创建。
    再管理这些Fragment的显示和隐藏:

      FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            if(!mFragmentManager.getFragments().contains(fragment)){
                fragmentTransaction.add(mId,fragment);
            }
    
            for(Fragment fragment1 :mFragmentManager.getFragments()){
                fragmentTransaction.hide(fragment1);
            }
            fragmentTransaction.show(fragment);
            fragmentTransaction.commit();
            
    

    为什么不使用replace方法呢:

      FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            fragmentTransaction.replace(mId,fragment);
            fragmentTransaction.commit();
    

    因为:

      Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
            for (int opNum = 0; opNum < mOps.size(); opNum++) {
                final Op op = mOps.get(opNum);
                switch (op.mCmd) {
                    case OP_ADD:
                    case OP_ATTACH:
                        added.add(op.mFragment);
                        break;
                    case OP_REMOVE:
                    case OP_DETACH: {
                        added.remove(op.mFragment);
                        if (op.mFragment == oldPrimaryNav) {
                            mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.mFragment));
                            opNum++;
                            oldPrimaryNav = null;
                        }
                    }
                    break;
                    case OP_REPLACE: {
                        final Fragment f = op.mFragment;
                        final int containerId = f.mContainerId;
                        boolean alreadyAdded = false;
                        for (int i = added.size() - 1; i >= 0; i--) {
                            final Fragment old = added.get(i);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    alreadyAdded = true;
                                } else {
                                    // This is duplicated from above since we only make
                                    // a single pass for expanding ops. Unset any outgoing primary nav.
                                    if (old == oldPrimaryNav) {
                                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
                                        opNum++;
                                        oldPrimaryNav = null;
                                    }
                                    final Op removeOp = new Op(OP_REMOVE, old);
                                    removeOp.mEnterAnim = op.mEnterAnim;
                                    removeOp.mPopEnterAnim = op.mPopEnterAnim;
                                    removeOp.mExitAnim = op.mExitAnim;
                                    removeOp.mPopExitAnim = op.mPopExitAnim;
                                    mOps.add(opNum, removeOp);
                                    added.remove(old);
                                    opNum++;
                                }
                            }
                        }
                        if (alreadyAdded) {
                            mOps.remove(opNum);
                            opNum--;
                        } else {
                            op.mCmd = OP_ADD;
                            added.add(f);
                        }
                    }
                    break;
                    case OP_SET_PRIMARY_NAV: {
                        // It's ok if this is null, that means we will restore to no active
                        // primary navigation fragment on a pop.
                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav));
                        opNum++;
                        // Will be set by the OP_SET_PRIMARY_NAV we inserted before when run
                        oldPrimaryNav = op.mFragment;
                    }
                    break;
                }
            }
            return oldPrimaryNav;
        }
    

    其中关于Replace操作的执行是:

      case OP_REPLACE: {
                        final Fragment f = op.mFragment;
                        final int containerId = f.mContainerId;
                        boolean alreadyAdded = false;
                        for (int i = added.size() - 1; i >= 0; i--) {
                            final Fragment old = added.get(i);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    alreadyAdded = true;
                                } else {
                                    // This is duplicated from above since we only make
                                    // a single pass for expanding ops. Unset any outgoing primary nav.
                                    if (old == oldPrimaryNav) {
                                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
                                        opNum++;
                                        oldPrimaryNav = null;
                                    }
                                    final Op removeOp = new Op(OP_REMOVE, old);
                                    removeOp.mEnterAnim = op.mEnterAnim;
                                    removeOp.mPopEnterAnim = op.mPopEnterAnim;
                                    removeOp.mExitAnim = op.mExitAnim;
                                    removeOp.mPopExitAnim = op.mPopExitAnim;
                                    mOps.add(opNum, removeOp);
                                    added.remove(old);
                                    opNum++;
                                }
                            }
                        }
                        if (alreadyAdded) {
                            mOps.remove(opNum);
                            opNum--;
                        } else {
                            op.mCmd = OP_ADD;
                            added.add(f);
                        }
                    }
                    break;
    

    如果已经添加了这个Fragment,就不做操作,如果没有添加,就执行add,无论添加还是没有添加都会遍历FragmentManager中已经有的Fragments(不包含当前要替换的,从Fragments中删除,且执行删除操作:

      mOps.add(opNum, removeOp);
      added.remove(old);
      opNum++;
    

    因此还是要使用Hide和show方法来实现:

    那么我们是怎么看源码的呢?怎么追踪到这个的呢:

    首先,查看FragmentTransaction中的replace:

       @NonNull
        public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment) {
            return replace(containerViewId, fragment, null);
        }
    
    

    进入replace:

     public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
                @Nullable String tag)  {
            if (containerViewId == 0) {
                throw new IllegalArgumentException("Must use non-zero containerViewId");
            }
            doAddOp(containerViewId, fragment, tag, OP_REPLACE);
            return this;
        }
    

    查看doAddOp:

      void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
           ........
            addOp(new Op(opcmd, fragment));
        }
    
      void addOp(Op op) {
            mOps.add(op);
            op.mEnterAnim = mEnterAnim;
            op.mExitAnim = mExitAnim;
            op.mPopEnterAnim = mPopEnterAnim;
            op.mPopExitAnim = mPopExitAnim;
        }
    
    

    replace()并没有做什么操作,那么主要的操作就是在commit中进行的:

    public abstract int commit();
    

    查找FragmentTransaction的实现类,在FragmentManager中:

        public FragmentTransaction beginTransaction() {
            return new BackStackRecord(this);
        }
    

    查看BackStackRecord的commit()进行了那些操作:

       @Override
        public int commit() {
            return commitInternal(false);
        }
    
       int commitInternal(boolean allowStateLoss) {
            if (mCommitted) throw new IllegalStateException("commit already called");
            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
                Log.v(TAG, "Commit: " + this);
                LogWriter logw = new LogWriter(TAG);
                PrintWriter pw = new PrintWriter(logw);
                dump("  ", pw);
                pw.close();
            }
            mCommitted = true;
            if (mAddToBackStack) {
                mIndex = mManager.allocBackStackIndex();
            } else {
                mIndex = -1;
            }
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
    

    根据:mManager.enqueueAction(this, allowStateLoss);定位:

     void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
            if (!allowStateLoss) {
                if (mHost == null) {
                    if (mDestroyed) {
                        throw new IllegalStateException("FragmentManager has been destroyed");
                    } else {
                        throw new IllegalStateException("FragmentManager has not been attached to a "
                                + "host.");
                    }
                }
                checkStateLoss();
            }
            synchronized (mPendingActions) {
                if (mHost == null) {
                    if (allowStateLoss) {
                        // This FragmentManager isn't attached, so drop the entire transaction.
                        return;
                    }
                    throw new IllegalStateException("Activity has been destroyed");
                }
                mPendingActions.add(action);
                scheduleCommit();
            }
        }
    

    定位到scheduleCommit:

     void scheduleCommit() {
            synchronized (mPendingActions) {
                boolean postponeReady =
                        mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
                boolean pendingReady = mPendingActions.size() == 1;
                if (postponeReady || pendingReady) {
                    mHost.getHandler().removeCallbacks(mExecCommit);
                    mHost.getHandler().post(mExecCommit);
                    updateOnBackPressedCallbackEnabled();
                }
            }
        }
    

    定位到mExecCommit

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

    定位到execPendingActions:

       boolean execPendingActions(boolean allowStateLoss) {
            ensureExecReady(allowStateLoss);
    
            boolean didSomething = false;
            while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
                mExecutingActions = true;
                try {
                    removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
                } finally {
                    cleanupExec();
                }
                didSomething = true;
            }
    
            updateOnBackPressedCallbackEnabled();
            doPendingDeferredStart();
            mFragmentStore.burpActive();
    
            return didSomething;
        }
    

    定位到removeRedundantOperationsAndExecute

     private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records,
                @NonNull ArrayList<Boolean> isRecordPop) {
            if (records.isEmpty()) {
                return;
            }
    
            if (records.size() != isRecordPop.size()) {
                throw new IllegalStateException("Internal error with the back stack records");
            }
    
            // Force start of any postponed transactions that interact with scheduled transactions:
            executePostponedTransaction(records, isRecordPop);
    
            final int numRecords = records.size();
            int startIndex = 0;
            for (int recordNum = 0; recordNum < numRecords; recordNum++) {
                final boolean canReorder = records.get(recordNum).mReorderingAllowed;
                if (!canReorder) {
                    // execute all previous transactions
                    if (startIndex != recordNum) {
                        executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                    }
                    // execute all pop operations that don't allow reordering together or
                    // one add operation
                    int reorderingEnd = recordNum + 1;
                    if (isRecordPop.get(recordNum)) {
                        while (reorderingEnd < numRecords
                                && isRecordPop.get(reorderingEnd)
                                && !records.get(reorderingEnd).mReorderingAllowed) {
                            reorderingEnd++;
                        }
                    }
                    executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
                    startIndex = reorderingEnd;
                    recordNum = reorderingEnd - 1;
                }
            }
            if (startIndex != numRecords) {
                executeOpsTogether(records, isRecordPop, startIndex, numRecords);
            }
        }
    

    定位到executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);

    private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records,
                @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
            final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
            boolean addToBackStack = false;
            if (mTmpAddedFragments == null) {
                mTmpAddedFragments = new ArrayList<>();
            } else {
                mTmpAddedFragments.clear();
            }
            mTmpAddedFragments.addAll(mFragmentStore.getFragments());
            Fragment oldPrimaryNav = getPrimaryNavigationFragment();
            for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
                final BackStackRecord record = records.get(recordNum);
                final boolean isPop = isRecordPop.get(recordNum);
                if (!isPop) {
                    oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
                } else {
                    oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
                }
                addToBackStack = addToBackStack || record.mAddToBackStack;
            }
            mTmpAddedFragments.clear();
    
            if (!allowReordering) {
                FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
                        false, mFragmentTransitionCallback);
            }
            executeOps(records, isRecordPop, startIndex, endIndex);
    
            int postponeIndex = endIndex;
            if (allowReordering) {
                ArraySet<Fragment> addedFragments = new ArraySet<>();
                addAddedFragments(addedFragments);
                postponeIndex = postponePostponableTransactions(records, isRecordPop,
                        startIndex, endIndex, addedFragments);
                makeRemovedFragmentsInvisible(addedFragments);
            }
    
            if (postponeIndex != startIndex && allowReordering) {
                // need to run something now
                FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                        postponeIndex, true, mFragmentTransitionCallback);
                moveToState(mCurState, true);
            }
    
            for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
                final BackStackRecord record = records.get(recordNum);
                final boolean isPop = isRecordPop.get(recordNum);
                if (isPop && record.mIndex >= 0) {
                    record.mIndex = -1;
                }
                record.runOnCommitRunnables();
            }
            if (addToBackStack) {
                reportBackStackChanged();
            }
        }
    

    定位到``:

        executeOps(records, isRecordPop, startIndex, endIndex);
        .......
        if (!isPop) {
                    oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
                } else {
                    oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
                }
    

    这样就可以查看到我们想要的代码逻辑了

    相关文章

      网友评论

          本文标题:【源码分析】FragmentTransaction.replac

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