This is an article written a long time ago, was published on another platform, moved to nuggets today. To be honest, I’ve been using Nuggets for almost a year now. There’s no clutter and the interface is simple and clean, which makes it a great place to write quietly.

The body of the

In this section, we’ll look at the internal code for FragmentManager and FragmentTransaction and see how they work together when we commit a transaction

Second, FragmentManager introduction

You can check out the FragmentManager website for details, and you can check out the FragmentManager source code for more details. Here I’ll briefly describe how when you want to get a FragmentManager in an Activity, The getFragmentManager() method we call directly

        FragmentManager fm = getFragmentManager();
Copy the code

If you only want to use FragmentManager, this line of code is perfectly sufficient, but if you want to know why, there are a few things to watch out for, because FragmentManager is an abstract class, So all we get is an implementation of the FragmentManager (subclass FragmentManagerImpl) object, which we can see below. This is part of our superclass Activity class

        final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

        /** * Return the FragmentManager for interacting with fragments associated * with this activity. */
        public FragmentManager getFragmentManager(a) {
            return mFragments.getFragmentManager();
        }
Copy the code

This is our FragmentController classes, we found that its again call mHost getFragmentManagerImpl () method

        private finalFragmentHostCallback<? > mHost;public static final FragmentController createController(FragmentHostCallback
        callbacks) {
            return new FragmentController(callbacks);
        }
        public FragmentManager getFragmentManager(a) {
            return mHost.getFragmentManagerImpl();
        }
Copy the code

This is our abstract class FragmentHostCallback< E >, which is composed of objects and returns a FragmentManagerImpl object

        final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();

        FragmentManagerImpl getFragmentManagerImpl(a) {
            return mFragmentManager;
        }
Copy the code

And what is the FragmentManagerImpl? This is the FragmentManagerImpl class that we finally got, which is an inner class

    public abstract class FragmentManager {...// omit the code
        final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {... }... }Copy the code

FindFragmentById: findFragmentById: findFragmentById: findFragmentById: findFragmentById: findFragmentById: findFragmentById

    final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
            /** Note that only parts of the FragmentManagerImpl code are shown as needed */
            ArrayList<Fragment> mAdded;// Manage a fragment queue
            ArrayList<BackStackRecord> mBackStack;// Manage a rollback stack

            @Override public FragmentTransaction beginTransaction(a) {
                return new BackStackRecord(this);
            }
            //addFragment, removeFragment, hideFragment, showFragment
            //detachFragment, attachFragment common methods
            public void addFragment(Fragment fragment, boolean moveToStateNow) {
                 // Add the fragment to the container and change the corresponding flag bit. }//findFragmentById and findFragmentByTag are common methods
            public Fragment findFragmentById(int id) {
                // Retrieve the fragment from the container based on the id. }Copy the code

I’m just going to show you a little bit of the FragmentManagerImpl code here to give you an idea of how many more methods we’ll be exposed to when we introduce the FragmentTransaction. Understand how the two work together when we commit a transaction.

3. Analysis of FragmentTransaction

Above we said FragmentManager, with the beginTransaction() method, we can start a transaction, and then we can do some normal operations

        private void addFragment4(a) {
            FragmentManager fm = getFragmentManager();
            // Add a local variable here for visual purposes
            FragmentTransaction fragmentTransaction = fm.beginTransaction();
            MyFragment4 fragment4 = (MyFragment4) fm.findFragmentById(R.id.activity5_fragment);
            if(fragment4 == null){
                fragment4 = new MyFragment4();
                fragmentTransaction.add(R.id.activity5_fragment, fragment4)
                        // Whether to add to the rollback stack as required
                        .addToBackStack(null) .commit(); }}Copy the code

We know that fm.beginTransaction() actually calls the method of its subclass FragmentManagerImpl, which is a polymorphic idea

        @Override public FragmentTransaction beginTransaction(a) {
            return new BackStackRecord(this);
        }
Copy the code

FragmentTransaction is an abstract transaction class that subclasses The BackStackRecord class. BackStackRecord has a lot of code in it. Here, I’ll take a quick look at BackStackRecord and see how it works with FragmentManagerImpl. This is our BackStackRecord class

    final class BackStackRecord extends FragmentTransaction implements 
                FragmentManager.BackStackEntry.Runnable {
            // Here we hold the FragmentManagerImpl object
            // Of course you can call the FragmentManagerImpl method as needed
            final FragmentManagerImpl mManager;

            public BackStackRecord(FragmentManagerImpl manager) { mManager = manager; }}Copy the code

This is our FragmentManagerImpl class, and when we beginTransaction() in our Activity, we know that we created a transaction BackStackRecord and passed a reference to itself

    final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
            /** Note that only parts of the FragmentManagerImpl code are shown as needed */
            ArrayList<Fragment> mAdded;// Manage a fragment queue

            @Override public FragmentTransaction beginTransaction(a) {
                return new BackStackRecord(this); }}Copy the code

We then call the Add (int var1, Fragment var2) method of the BackStackRecord class

        public FragmentTransaction add(int containerViewId, Fragment fragment) {
            //doAddOp(...) This is a private method
            // An Op object is created in the method
            // The Op object records the actions and Fragment references of an operation and the animation used for the operation
            doAddOp(containerViewId, fragment, null, OP_ADD);
            return this;
        }
Copy the code

We then call addToBackStack(NULL) of the BackStackRecord class to add the unstack method

        public FragmentTransaction addToBackStack(String name) {
            if(! mAllowAddToBackStack) {throw newAn IllegalStateException ("This FragmentTransaction is not allowed to be added to the back stack.")}// We find that this method only changes the flag bit, and there is no actual logical code
            // This is why we must add the rollback stack before commit
            // Transactions that do not change flag bits are not committed because they are joined after commit
            mAddToBackStack = true;
            mName = name;
            return this;
        }
Copy the code

We then call commit() in the BackStackRecord class to commit our transaction

        public int commit(a) {
            return commitInternal(false);
        }
        // If the lifecycle is after the Saving Activity when the commit is made
        // Using commit will throw an error due to missing information
        // If you do not need to save information, use commitAllowingStateLoss
        public int commitAllowingStateLoss(a) {
            return commitInternal(true);
        }
        // allowStateLoss is false
        int commitInternal(boolean allowStateLoss) {...// Omit some code
            if (mAddToBackStack) {
                // Use mAvailBackStackIndices and mBackStackIndices
                // to assign an Index to BackStackRecord
                mIndex = mManager.allocBackStackIndex(this);
            } else {
                Index = -1;
            }
            // This is the last method we commit, calling the enqueueAction method of the FragmentManagerImpl class
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
Copy the code

AllocBackStackIndex (…) of FragmentManagerImpl class Method when added to the return stack and before committing the transaction

        // Must be accessed while locked.
        ArrayList<BackStackRecord> mBackStackIndices;
        ArrayList<Integer> mAvailBackStackIndices;

        public int allocBackStackIndex(BackStackRecord bse) {
            synchronized (this) {
                if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
                    if (mBackStackIndices == null) {
                        mBackStackIndices = new ArrayList<BackStackRecord>();
                    }
                    int index = mBackStackIndices.size();
                    mBackStackIndices.add(bse);
                    return index;
                } else {
                    int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
                    mBackStackIndices.set(index, bse);
                    returnindex; }}}Copy the code

This is enqueueAction(…) of the FragmentManagerImpl class that we called after the last commit. methods

        ArrayList<Runnable> mPendingActions;// Each transaction committed is in a different thread
        Runnable mExecCommit = new Runnable() {
            @Override
            public void run(a) {
                // I won't go into detail about this method, its main function
                // Iterate over the transaction threads managed by mPendingActions
                // Call the run method of the per-thread transaction (BackStackRecord) class
                // Then remove the corresponding transaction threads one by oneexecPendingActions(); }};public void enqueueAction(Runnable action, boolean allowStateLoss) {
            if(! allowStateLoss) { checkStateLoss(); }synchronized (this) {
                if (mDestroyed || mHost == null) {
                    throw new IllegalStateException("Activity has been destroyed");
                }
                if (mPendingActions == null) {
                    // This is a collection of threads that manage transactions. Each thread represents a transaction operation to be processed
                    mPendingActions = new ArrayList<Runnable>();
                }
                mPendingActions.add(action);
                if (mPendingActions.size() == 1) {
                    mHost.getHandler().removeCallbacks(mExecCommit);
                    // mhost. getHandler() returns a handler object
                    / / the handler. Post (Runnable action)
                    // So the run method of mExecCommit is calledmHost.getHandler().post(mExecCommit); }}}Copy the code

This is the Run method of our BackStackRecord class, where we decide what action to take based on the information carried by the Op object, and then call the FragmentManagerImpl method, moveToState(…). And whether the transaction needs to be added to the rollback stack

        public void run(a) {...Call the corresponding operation according to the OP's information, such as when we add operation
                switch (op.cmd) {
                    case OP_ADD:
                        Fragment f = op.fragment;
                        f.mNextAnim = op.enterAnim;
                        // Add the fragment of the current operation to the FragmentManager managed list
                        mManager.addFragment(f, false);
                        break;
                    case OP_REMOVE:
                        Fragment f = op.fragment;
                        f.mNextAnim = op.exitAnim;
                        mManager.removeFragment(f, mTransition, mTransitionStyle);
                        break; . }... mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle,true);
                if (mAddToBackStack) {
                        // Add this transaction to the rollback stack
                       mManager.addBackStackState(this); }}Copy the code

So finally we’ll come back to the partial methods of the FragmentManagerImpl class

            public void addFragment(Fragment fragment, boolean moveToStateNow) {
                // Add the fragment to the management queue and change the corresponding flag bit. }// Add a transaction BackStackRecord to the rollback stack
            void addBackStackState(BackStackRecord state) {
                if (mBackStack == null) {
                    mBackStack = new ArrayList<BackStackRecord>();
                }
                mBackStack.add(state);
                / / callback onBackStackChanged
                reportBackStackChanged();
            }
            // This method is very important. It is called whenever the state changes during the Fragment's life cycle
            // This also ensures that the Fragment and Activity are in sync
            // Specific code, interested can view the source code to understand
            void moveToState(int newState, int transit, int transitStyle, boolean always) {
               //Fragment indicates the operation logic for state switching. }Copy the code

To analysis ended here, open and submit a transaction, whether the transaction is added to the return stack two cases, we together to do the introduction, we look at the below, when we press the return key, when a transaction in the case of return stack, how transactions are ejected stack, this is our Activity code

        public void onBackPressed(a) {
            if(mActionBar ! =null && mActionBar.collapseActionView()) {
                return;
            }
            // Return true if our Fragment's transaction rollback stack can still be popped, false otherwise
            if(! mFragments.getFragmentManager().popBackStackImmediate()) {// The finish() method is called after some other criteriafinishAfterTransition(); }}Copy the code

The FragmentManagerImpl class popBackStackImmediate(…) methods

         @Override public boolean popBackStackImmediate(a) {
            checkStateLoss();
            // If there are pending transactions, return true
            executePendingTransactions();
            // The parameter is fixed
            return popBackStackState(mHost.getHandler(), null, -1.0);
        }
Copy the code

This is popBackStackState(…) of the FragmentManagerImpl class. methods

        // If 1 is moved 0 bits to the left, it is still 1, and 0&1=0.
        public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;

        boolean popBackStackState(Handler handler, String name, int id, int flags) {
            // If our rollback is null, return false to exit the Activity
            if (mBackStack == null) {
                return false;
            }
            // When the key is returned, the parameter is fixed, so go directly to the branch
            if(if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0)) {
                int last = mBackStack.size()-1;
                if (last < 0) {
                    return false;
                }
                // Remove the top layer of the rollback stack we manage
                final BackStackRecord bss = mBackStack.remove(last);
                SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
                SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
                // Iterate through all Op fragments to find the first fragment removed
                // And the last fragment added
                bss.calculateBackFragments(firstOutFragments, lastInFragments);
                // This is the main method, see the comments below
                bss.popFromBackStack(true.null, firstOutFragments, lastInFragments);
                / / FragmentManagerImpl manages an ArrayList < OnBackStackChangedListener > collection
                // This method loops through the onBackStackChanged() method
                reportBackStackChanged();
            }else{... }}Copy the code

This is the popFromBackStack we need for the BackStackRecord class (…) methods

        public TransitionState popFromBackStack(boolean doStateMove, TransitionState state
                , SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {...// Based on the information carried by the transaction's Op, know what operation is in the transaction rollback stack, e.g. Add
            // The type of transaction in the stack is known
            // If transaction A comes from add, execute remove after back;
            // If transaction A comes from replace, then remove the new one and add the old one after back
            Op op = mTail;
            while(op ! =null) {
                switch (op.cmd) {
                    case OP_ADD: {
                        Fragment f = op.fragment;
                        f.mNextAnim = op.popExitAnim;
                        // Because the Op flag in the transaction in the rollback stack indicates that this is an add operation
                        // The back key removeFragment the transaction
                        mManager.removeFragment(f
                            , FragmentManagerImpl.reverseTransit(mTransition)
                            , mTransitionStyle);
                    }
                    break; .// Other operations}}... }Copy the code

Ok, end of article.