In daily development, we often use fragments to manage the layout, which is very convenient to use, but many people don’t know what operations are hidden behind the simple API.

If you can’t answer these questions, this article might help you:

  • Fragment FragmentManager Relationship and role of FragmentTransaction
  • How does Fragment add and replace layouts
  • The principle of nested Fragments

After reading this article, you will know:

  • The use of fragments
  • FragmentManager
    • Defined operations
    • Inner class interface
    • The implementation class FragmentManagerImpl
  • The transaction
    • FragmentTransaction
    • Four ways to commit a transaction
    • The transaction really implements the BackStackRecord back stack
    • The part that you actually deal with
  • What is the fragments
    • The main member of Fragment
    • Lifecycle approach
  • conclusion
    • FragmentFragmentManagerFragmentTransaction relationship
    • How does Fragment add and replace layouts
    • The principle of nested Fragments

App. fragment “support-fragment-25.3.1-source.jar” and related classes

The use of fragments

Fragment: Fragment: Fragment: Fragment: Fragment: Fragment: Fragment: Fragment: Fragment

The interface is a little ugly, but the meaning is similar, ha, a list on the left, click to switch the layout on the right. We can do that using fragments.

The implementation is simple: create a layout and then replace the Fragment when clicked in the Activity.

mFragmentManager = getSupportFragmentManager();
mFragmentManager.beginTransaction(a).replace(R.id.fl_content, fragment)
    .commitAllowingStateLoss(a);Copy the code

The code is simple, with three core steps:

  1. Create fragments
  2. Get FragmentManager
  3. Invoke transactions, add, replace

Let’s walk through the story step by step.

Fragment that you’re familiar with, at the end.

Take a look at FragmentManager first.

FragmentManager

public abstract class FragmentManager {.}Copy the code

FragmentManager is an abstract class that defines fragment-related operations and internal classes/interfaces.

Defined operations

The methods defined in FragmentManager are as follows:

// Enable a series of operations on Fragments
public abstract FragmentTransaction beginTransaction(a);/ / FragmentTransaction.com () is executed asynchronously at MIT, if you want to immediate execution, you can call this method
public abstract boolean executePendingTransactions(a);// Find fragments parsed from XML or added to transactions by ID
// The FragmentManager will be added to the FragmentManager first
public abstract Fragment findFragmentById(@IdRes int id);

// This is similar to the above, except that we use tag to search
public abstract Fragment findFragmentByTag(String tag);

// The Fragment at the top of the stack is displayed asynchronously
public abstract void popBackStack(a);// Immediately pop back to the top of the stack
public abstract boolean popBackStackImmediate(a);// Return the Fragment that matches the name at the top of the stack. If the name passed in is not empty and a Fragment is found in the middle of the stack, all fragments above the Fragment will be displayed
// It has a singleTask feel similar to boot mode
// If the name passed in is null, it is the same as popBackStack()
// Execute asynchronously
public abstract void popBackStack(String name, int flags);

// The sync version above
public abstract boolean popBackStackImmediate(String name, int flags);

// The same as using name to find and pop
/ / here is the id of the id is FragmentTransaction.com MIT () returns
public abstract void popBackStack(int id, int flags);
/ / you know
public abstract boolean popBackStackImmediate(int id, int flags);

// Get the number of elements in the rollback stack
public abstract int getBackStackEntryCount(a);// Retrieves an element in the rollback stack based on the index
public abstract BackStackEntry getBackStackEntryAt(int index);

// Add or remove a listener
public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);

// Also defines passing an instance of Fragment as an argument
public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
public abstract Fragment getFragment(Bundle bundle, String key);

// Get all fragments added to the manager
public abstract List<Fragment> getFragments(a);Copy the code

As you can see, many of the methods defined are executed asynchronously. See how it is implemented asynchronously later.

Inner class/interface:

  • BackStackEntry: Fragment Reverses an element in the stack
  • OnBackStackChangedListener: back stack change listener
  • Listening FragmentLifecycleCallbacks: FragmentManager fragments of life cycle
// Back an element in the stack
public interface BackStackEntry {
    // The unique identifier of the element in the stack
    public int getId(a);// get the name set by FragmentTransaction#addToBackStack(String)
    public String getName(a);@StringRes
    public int getBreadCrumbTitleRes(a);@StringRes
    public int getBreadCrumbShortTitleRes(a);public CharSequence getBreadCrumbTitle(a);public CharSequence getBreadCrumbShortTitle(a); }Copy the code

As you can see, BackStackEntry’s interface is relatively simple, and the key information is ID and Name.

// Callback when there are changes in the Fragment rollback stack
public interface OnBackStackChangedListener {
    public void onBackStackChanged(a); }Copy the code
//Fragment lifecycle listening in FragmentManager
    public abstract static class FragmentLifecycleCallbacks {
        public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
        public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
        public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
                Bundle savedInstanceState) {}
        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
                Bundle savedInstanceState) {}
        public void onFragmentStarted(FragmentManager fm, Fragment f) {}
        public void onFragmentResumed(FragmentManager fm, Fragment f) {}
        public void onFragmentPaused(FragmentManager fm, Fragment f) {}
        public void onFragmentStopped(FragmentManager fm, Fragment f) {}
        public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
        public void onFragmentDetached(FragmentManager fm, Fragment f) {}
    }
}Copy the code

This interface will look familiar to those of you familiar with the Fragment life cycle. The purpose of this interface is to provide a callback to all Fragment life cycle changes in the FragmentManager.

Summary:

As you can see, FragmentManager is an abstract class that defines the operation and management of the list of fragments that are added to an Activity/Fragment, and the stack rollback of the Fragment.

The implementation class FragmentManagerImpl

Tasks defined by the FragmentManager are implemented by the FragmentManagerImpl.

Main members:

final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {

    ArrayList<OpGenerator> mPendingActions;
    Runnable[] mTmpActions;
    boolean mExecutingActions;

    ArrayList<Fragment> mActive;
    ArrayList<Fragment> mAdded;
    ArrayList<Integer> mAvailIndices;
    ArrayList<BackStackRecord> mBackStack;
    ArrayList<Fragment> mCreatedMenus;

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

    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
/ /...
}Copy the code

As you can see, added, active is defined in FragmentManagerImpl. And a list of unstacks, as required by FragmentManager.

int mCurState = Fragment.INITIALIZING;
FragmentHostCallback mHost;
FragmentContainer mContainer;
Fragment mParent;

static Field sAnimationListenerField = null;

boolean mNeedMenuInvalidate;
boolean mStateSaved;
boolean mDestroyed;
String mNoTransactionsBecause;
boolean mHavePendingDeferredStart;Copy the code

Then there’s the current state, the mParent of the current Fragment, and the mHost and mContainer of the FragmentManager.

FragmentContainer is an interface that defines two methods for layout:

public abstract class FragmentContainer {
    @Nullable
    public abstract View onFindViewById(@IdRes int id);
    public abstract boolean onHasView(a); }Copy the code

The FragmentHostCallback is a bit more complicated. It provides the information the Fragment needs and defines what the Fragment host should do:

public abstract class FragmentHostCallback<E> extends FragmentContainer {
    private final Activity mActivity;
    final Context mContext;
    private final Handler mHandler;
    final int mWindowAnimations;
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    / /...
}Copy the code

As we know, there are generally two types of Fragment hosts:

  1. Activity
  2. Fragment

For example, the FragmentActivity inner class HostCallbacks implements this abstract class:

class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
    public HostCallbacks() {
        super(FragmentActivity.this /*fragmentActivity*/);
    }
    / /...

    @Override
    public LayoutInflater onGetLayoutInflater() {
        return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
    }

    @Override
    public FragmentActivity onGetHost() {
        return FragmentActivity.this;
    }

    @Override
    public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
    }

    @Override
    public void onStartActivityFromFragment(
            Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
        FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
    }

    @Override
    public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
            @NonNull String[] permissions, int requestCode) {
        FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
                requestCode);
    }

    @Override
    public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
        return ActivityCompat.shouldShowRequestPermissionRationale(
                FragmentActivity.this, permission);
    }

    @Override
    public boolean onHasWindowAnimations() {
        returngetWindow() ! =null;
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        FragmentActivity.this.onAttachFragment(fragment);
    }

    @Nullable
    @Override
    public View onFindViewById(int id) {
        return FragmentActivity.this.findViewById(id);
    }

    @Override
    public boolean onHasView() {
        final Window w = getWindow();
        return(w ! =null&& w.peekDecorView() ! =null); }}Copy the code

Let’s take a look at how the key methods he defines for FragmentManager are implemented.

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

BeginTransaction () returns a new BackStackRecord, which we’ll cover later.

As mentioned earlier, popBackStack() is an asynchronous operation. How does it do that?

@Override
public void popBackStack() {
    enqueueAction(new PopBackStackState(null, -1.0), false);
}
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 = newArrayList<>(); } mPendingActions.add(action); scheduleCommit(); }}private void scheduleCommit() {
    synchronized (this) {
        booleanpostponeReady = mPostponedTransactions ! =null && !mPostponedTransactions.isEmpty();
        booleanpendingReady = mPendingActions ! =null && mPendingActions.size() == 1;
        if(postponeReady || pendingReady) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); }}}Copy the code

The Handler in the host is called to send the task. Other asynchronous implementations are similar, so I won’t go into details here.

Back stack related methods:

ArrayList<BackStackRecord> mBackStack;
@Override
public int getBackStackEntryCount() {
    returnmBackStack ! =null ? mBackStack.size() : 0;
}

@Override
public BackStackEntry getBackStackEntryAt(int index) {
    return mBackStack.get(index);
}Copy the code

As you can see, the BackStackRecord that starts a transaction and goes back to the stack and returns/operates is BackStackRecord. Let’s see what that is.

The transaction

BackStackRecord inherits FragmentTransaction:

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry.FragmentManagerImpl.OpGenerator {. }Copy the code

Take a look at FragmentTransaction first.

FragmentTransaction

FragmentTransaction defines a set of methods for manipulating fragments:

// It calls add(int, Fragment, String), where the first argument is passed 0
public abstract FragmentTransaction add(Fragment fragment, String tag);

// It calls add(int, Fragment, String), where the third argument is null
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);

// Add a Fragment to the final implementation of the Activity
// The first parameter indicates the id of the layout where the Fragment is placed
// The second parameter indicates the Fragment to be added. A Fragment can be added only once
// Add a tag to the Fragment. You can use this tag to query the Fragment
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment,
        @Nullable String tag);

// Call replace(int, Fragment, String) and pass null as the third argument
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);

// Replace an existing fragment in the host
// This method is equivalent to calling remove() and then add().
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment,
        @Nullable String tag);

// Remove an existing fragment
// If it was previously added to the host, its layout will be removed
public abstract FragmentTransaction remove(Fragment fragment);

// Hide an existing fragment
// Hide the layout added to the host
public abstract FragmentTransaction hide(Fragment fragment);

// Displays the previously hidden fragment. This only applies to the fragment that was previously added to the host
public abstract FragmentTransaction show(Fragment fragment);

// Remove the specified fragment from the layout
// When this method is called, the fragment layout is destroyed
public abstract FragmentTransaction detach(Fragment fragment);

// Call this method to rebind a fragment's layout after it has been unbound
// This will cause the fragment's layout to be rebuilt, then added and displayed on the interface
public abstract FragmentTransaction attach(Fragment fragment);Copy the code

To complete the fragment operation, we need to submit the following:

mFragmentManager.beginTransaction(a).replace(R.id.fl_child, getChildFragment())
//        .commit(a).commitAllowingStateLoss(a);Copy the code

Four ways to commit a transaction

There are four final ways to commit a transaction:

  1. commit()
  2. commitAllowingStateLoss()
  3. commitNow()
  4. commitNowAllowingStateLoss()

The characteristics and differences between them are as follows:

public abstract int commit(a);Copy the code

Commit () is executed asynchronously on the main thread, which is essentially a Handler throwing a task and waiting for the main thread to execute it.

Note: Commit () needs to be called before the host Activity saves state, otherwise an error will be reported. This is because if the Activity fails and needs to restore its state, the commit() after saving the state will be lost, which is not the purpose of the call, so an error will be reported.

public abstract int commitAllowingStateLoss(a);Copy the code

CommitAllowingStateLoss () is also executed asynchronously, but it is different in that it allows it to be called after the Activity has saved state, meaning that it does not report state loss.

So we usually use it when it’s acceptable to have an interface state error.

public abstract void commitNow(a);Copy the code

CommitNow () is executed synchronously and immediately commits the task.

Previously mentioned FragmentManager. ExecutePendingTransactions () can also be implemented immediately to commit the transaction. However, we generally recommend using commitNow(), because the other commitNow() is executing all pending tasks at once, and may execute all current transactions at once, which may have side effects.

In addition, transactions committed by this method may not be added to the FragmentManger back stack, because you may affect the order of other asynchronously executed tasks in the stack by committing them directly.

Like commit(), commitNow() must be called before the Activity saves state; otherwise, an exception is thrown.

public abstract void commitNowAllowingStateLoss(a);Copy the code

CommitAllowingStateLoss () for synchronous execution.

OK, with the FragmentTransaction operation defined, let’s look at the BackStackRecord returned from beginTransaction() that we really care about:

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

Transactions really implement/back stack BackStackRecord

BackStackRecord is both a true implementation of the transaction that operates on the Fragment and an implementation of the rollback stack in FragmentManager:

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry.FragmentManagerImpl.OpGenerator {. }Copy the code

Its key members:


final FragmentManagerImpl mManager;

//Op optional state value
static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
static final int OP_HIDE = 4;
static final int OP_SHOW = 5;
static final int OP_DETACH = 6;
static final int OP_ATTACH = 7;

ArrayList<Op> mOps = new ArrayList<>();
static final class Op {
    int cmd;    / / state
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;
}

int mIndex = -1;    // Index of the last element in the stackCopy the code

You can see that an Op is a Fragment that adds state and animation information, and mOps is all the fragments in the stack.

The method of transaction definition how it’s implemented.

Let’s look at the implementation of adding a Fragment to the layout add() :

@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

@Override
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    final Class fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    if(fragmentClass.isAnonymousClass() || ! Modifier.isPublic(modifiers) || (fragmentClass.isMemberClass() && ! Modifier.isStatic(modifiers))) {throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                + " must be a public static class to be properly recreated from"
                + " instance state.");
    }

    //1. Change the added fragmentManager to the current stack manager
    fragment.mFragmentManager = mManager;

    if(tag ! =null) {
        if(fragment.mTag ! =null && !tag.equals(fragment.mTag)) {
            throw new IllegalStateException("Can't change tag of fragment "
                    + fragment + ": was " + fragment.mTag
                    + " now " + tag);
        }
        fragment.mTag = tag;
    }

    if(containerViewId ! =0) {
        if (containerViewId == View.NO_ID) {
            throw new IllegalArgumentException("Can't add fragment "
                    + fragment + " with tag " + tag + " to container view with no id");
        }
        if(fragment.mFragmentId ! =0&& fragment.mFragmentId ! = containerViewId) {throw new IllegalStateException("Can't change container ID of fragment "
                    + fragment + ": was " + fragment.mFragmentId
                    + " now " + containerViewId);
        }
        //2. Set host ID to layout ID
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }

    / / 3. Structural Op
    Op op = new Op();
    op.cmd = opcmd;    / / state
    op.fragment = fragment;
    //4. Add to the arraylist
    addOp(op);
}
void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}Copy the code

As you can see, adding a Fragment to the layout is simple. Here’s a summary: Modify the fragmentManager and ID, construct an Op, set the state information, and add it to the list.

Replace replace:

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

@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
    if (containerViewId == 0) {
        throw new IllegalArgumentException("Must use non-zero containerViewId");
    }

    doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    return this;
}Copy the code

Call doAddOp() with OP_REPLACE as the fourth argument.

Look at other methods of implementation is very simple, nothing more than to construct an Op, set the corresponding state value.

@Override
public FragmentTransaction remove(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_REMOVE;
    op.fragment = fragment;
    addOp(op);

    return this;
}

@Override
public FragmentTransaction hide(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_HIDE;
    op.fragment = fragment;
    addOp(op);

    return this;
}

@Override
public FragmentTransaction show(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_SHOW;
    op.fragment = fragment;
    addOp(op);

    return this;
}Copy the code

So when do these differences in state values matter?

Don’t forget that we have one last step in Fragment manipulation, commit.

See how these two are implemented:

@Override
public int commit() {
    return commitInternal(false);
}

@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}
int commitInternal(boolean allowStateLoss) {
    if (mCommitted) throw new IllegalStateException("commit already called");
    / /...
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);    // Update index information
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);    // Asynchronous tasks are queued
    return mIndex;
}
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();    // Send the task}}private void scheduleCommit() {
    synchronized (this) {
        booleanpostponeReady = mPostponedTransactions ! =null && !mPostponedTransactions.isEmpty();
        booleanpendingReady = mPendingActions ! =null && mPendingActions.size() == 1;
        if(postponeReady || pendingReady) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); }}}Copy the code

It has already been introduced, FragmentManager. EnqueueAction () is ultimately used asynchronous execution of Handler implementation.

Now the question is what is the mission?

The answer is the task mExecCommit sent by the Handler:

Runnable mExecCommit = new Runnable() {
    @Override
    public void run() { execPendingActions(); }};/** * Only call from main thread! * Update UI in main thread */
public boolean execPendingActions() {
    ensureExecReady(true);

    boolean didSomething = false;
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);    // This is the entrance
        } finally {
            cleanupExec();
        }
        didSomething = true;
    }

    doPendingDeferredStart();

    return didSomething;
}
private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop) {
    if (records == null || records.isEmpty()) {
        return;
    }

    if (isRecordPop == null|| 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 canOptimize = records.get(recordNum).mAllowOptimization;
        if(! canOptimize) {// execute all previous transactions
            if(startIndex ! = recordNum) {// Filter the Ops
                executeOpsTogether(records, isRecordPop, startIndex, recordNum);
            }
            // execute all unoptimized pop operations together or one add operation
              / /...
    }
    if (startIndex != numRecords) {
        executeOpsTogether(records, isRecordPop, startIndex, numRecords);
    }
}
private void executeOpsTogether(ArrayList<BackStackRecord> records,
        ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
    boolean addToBackStack = false;
    if (mTmpAddedFragments == null) {
        mTmpAddedFragments = new ArrayList<>();
    } else {
        mTmpAddedFragments.clear();
    }
    if(mAdded ! =null) {
        mTmpAddedFragments.addAll(mAdded);
    }
    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        final BackStackRecord record = records.get(recordNum);
        final boolean isPop = isRecordPop.get(recordNum);
        if(! isPop) { record.expandReplaceOps(mTmpAddedFragments);// Change the status
        } else {
            record.trackAddedFragmentsInPop(mTmpAddedFragments);    
        }
        addToBackStack = addToBackStack || record.mAddToBackStack;
    }
    mTmpAddedFragments.clear();

    if(! allowOptimization) { FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
                false);
    }
    // Real processing entry
    executeOps(records, isRecordPop, startIndex, endIndex);

    int postponeIndex = endIndex;
    if (allowOptimization) {
        ArraySet<Fragment> addedFragments = new ArraySet<>();
        addAddedFragments(addedFragments);
        postponeIndex = postponePostponableTransactions(records, isRecordPop,
                startIndex, endIndex, addedFragments);
        makeRemovedFragmentsInvisible(addedFragments);    // The name can be seen
    }

    if(postponeIndex ! = startIndex && allowOptimization) {// need to run something now
        FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                postponeIndex, true);
        moveToState(mCurState, true);
    }
    / /...
}
// Modify the Ops state, this step does not really deal with the state
expandReplaceOps(ArrayList<Fragment> added) {
    for (int opNum = 0; opNum < mOps.size(); opNum++) {
        final Op op = mOps.get(opNum);
        switch (op.cmd) {
            case OP_ADD:
            case OP_ATTACH:
                added.add(op.fragment);
                break;
            case OP_REMOVE:
            case OP_DETACH:
                added.remove(op.fragment);
                break;
            case OP_REPLACE: {     
                Fragment f = op.fragment;
                int containerId = f.mContainerId;
                boolean alreadyAdded = false;
                for (int i = added.size() - 1; i >= 0; i--) {
                    Fragment old = added.get(i);
                    if (old.mContainerId == containerId) {
                        if (old == f) {
                            alreadyAdded = true;
                        } else {
                            Op removeOp = new Op();
                            removeOp.cmd = OP_REMOVE;    // As you can see, the substitution is also done by deletingremoveOp.fragment = old; removeOp.enterAnim = op.enterAnim; removeOp.popEnterAnim = op.popEnterAnim; removeOp.exitAnim = op.exitAnim; removeOp.popExitAnim = op.popExitAnim; mOps.add(opNum, removeOp); added.remove(old); opNum++; }}}if (alreadyAdded) {
                    mOps.remove(opNum);
                    opNum--;
                } else{ op.cmd = OP_ADD; added.add(f); }}break; }}}// Set the final implementation of the Fragment to be removed to invisible
private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) {
    final int numAdded = fragments.size();
    for (int i = 0; i < numAdded; i++) {
        final Fragment fragment = fragments.valueAt(i);
        if(! fragment.mAdded) {final View view = fragment.getView();    // Get the Fragment layout and set the state
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                fragment.getView().setVisibility(View.INVISIBLE);
            } else {        // High version set transparency
                fragment.mPostponedAlpha = view.getAlpha();
                view.setAlpha(0f); }}}}Copy the code

It was a bit too much code, but we finally found the final implementation: The Handler asynchronously sent to the main line, scheduled execution, aggregated and modified the state of the Ops, and then traversed and modified the state of the Views in the Fragment stack.

The part that you actually deal with

The previous part mainly modified the state of the Fragment’s packaging Ops class. The real operation is performed according to the STATE of Ops in this part:

/** * Executes the operations contained within this transaction. The Fragment states will only * be modified if optimizations are not allowed. */
void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        f.setNextTransition(mTransition, mTransitionStyle);
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        if(! mAllowOptimization && op.cmd ! = OP_ADD) { mManager.moveFragmentToExpectedState(f); }}if(! mAllowOptimization) {// Added fragments are added at the end to comply with prior behavior.
        mManager.moveToState(mManager.mCurState, true); }}Copy the code

FragmentManager implements these methods simply by modifying the Fragment state value, such as remove(Fragment):

public void removeFragment(Fragment fragment) {
    if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
    final booleaninactive = ! fragment.isInBackStack();if(! fragment.mDetached || inactive) {if(mAdded ! =null) {
            mAdded.remove(fragment);
        }
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        fragment.mAdded = false;    // Set the property value
        fragment.mRemoving = true; }}Copy the code

This will eventually call moveToState(), so let’s get straight to its implementation:

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    // Unadded fragments are in onCreate() state
    if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
        newState = Fragment.CREATED;
    }
    if (f.mRemoving && newState > f.mState) {
        // While removing a fragment, we can't change it to a higher state.
        newState = f.mState;
    }
    // Delay startup is set to stop
    if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
        newState = Fragment.STOPPED;
    }
    if (f.mState < newState) {
        // For fragments that are created from a layout, when restoring from
        // state we don't want to allow them to be created until they are
        // being reloaded from the layout.
        if (f.mFromLayout && !f.mInLayout) {
            return;
        }
        if (f.getAnimatingAway() ! = null) {
            // The fragment is currently being animated... but! Now we
            // want to move our state back up. Give up on waiting for the
            // animation, move to whatever the final state should be once
            // the animation is done, and then we can proceed from there.
            f.setAnimatingAway(null);
            // If the Fragment is currently animated, modify it to its final state
            moveToState(f, f.getStateAfterAnimating(), 0.0.true);
        }
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (DEBUG) Log.v(TAG."moveto CREATED: " + f);
                if (f.mSavedFragmentState ! = null) {
                    f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                    f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                            FragmentManagerImpl.VIEW_STATE_TAG);
                    f.mTarget = getFragment(f.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG);
                    if (f.mTarget ! = null) {
                        f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                    }
                    f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                            FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                    if (!f.mUserVisibleHint) {
                        f.mDeferStart = true;
                        if (newState > Fragment.STOPPED) {
                            newState = Fragment.STOPPED;
                        }
                    }
                }
                f.mHost = mHost;
                f.mParentFragment = mParent;
                f.mFragmentManager = mParent ! = null
                        ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                f.mCalled = false;
                f.onAttach(mHost.getContext());    // Call the Fragment lifecycle method
                if (!f.mCalled) {
                    throw new SuperNotCalledException("Fragment " + f
                            + " did not call through to super.onAttach()");
                }
                if (f.mParentFragment = = null) {
                    mHost.onAttachFragment(f);
                } else {
                    f.mParentFragment.onAttachFragment(f);
                }
                dispatchOnFragmentAttached(f, mHost.getContext(), false);

                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState); // Call the Fragment lifecycle method

                    dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                } else {
                    f.restoreChildFragmentState(f.mSavedFragmentState);
                    f.mState = Fragment.CREATED;
                }
                f.mRetaining = false;
                if (f.mFromLayout) {    // from the layout
                    // For fragments that are part of the content view
                    // layout, we need to instantiate the view immediately
                    // and the inflater will take care of adding it.
                    f.mView = f.performCreateView(f.getLayoutInflater(    // Call the Fragment lifecycle method
                            f.mSavedFragmentState), null, f.mSavedFragmentState);
                    if (f.mView ! = null) {
                        f.mInnerView = f.mView;
                        if (Build.VERSION.SDK_INT > = 11) {
                            ViewCompat.setSaveFromParentEnabled(f.mView, false);
                        } else {
                            f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                        }
                        if (f.mHidden) f.mView.setVisibility(View.GONE);
                        f.onViewCreated(f.mView, f.mSavedFragmentState);    // Call the Fragment lifecycle method
                        dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
                    } else {
                        f.mInnerView = null; }}case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                    if (DEBUG) Log.v(TAG."moveto ACTIVITY_CREATED: " + f);
                    if (!f.mFromLayout) {
                        ViewGroup container = null;
                        if (f.mContainerId ! = 0) {
                            if (f.mContainerId = = View.NO_ID) {
                                throwException(new IllegalArgumentException(
                                        "Cannot create fragment "
                                                + f
                                                + " for a container view with no id"));
                            }
                            container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                            if (container = = null && !f.mRestored) {
                                String resName;
                                try {
                                    resName = f.getResources().getResourceName(f.mContainerId);
                                } catch (NotFoundException e) {
                                    resName = "unknown";
                                }
                                throwException(new IllegalArgumentException(
                                        "No view found for id 0x"
                                        + Integer.toHexString(f.mContainerId) + "("
                                        + resName
                                        + ") for fragment " + f));
                            }
                        }
                        f.mContainer = container;
                        f.mView = f.performCreateView(f.getLayoutInflater( // Call the Fragment lifecycle method
                                f.mSavedFragmentState), container, f.mSavedFragmentState);
                        if (f.mView ! = null) {
                            f.mInnerView = f.mView;
                            if (Build.VERSION.SDK_INT > = 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }
                            if (container ! = null) {
                                container.addView(f.mView);       // Add the Fragment layout to the parent layout
                            }
                            if (f.mHidden) {
                                f.mView.setVisibility(View.GONE);
                            }
                            f.onViewCreated(f.mView, f.mSavedFragmentState);// Call the Fragment lifecycle method
                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                    false);
                            // Only animate the view if it is visible. This is done after
                            // dispatchOnFragmentViewCreated in case visibility is changed
                            f.mIsNewlyAdded = (f.mView.getVisibility() = = View.VISIBLE)
                                    && f.mContainer ! = null;
                        } else {
                            f.mInnerView = null;
                        }
                    }

                    f.performActivityCreated(f.mSavedFragmentState); // Call the Fragment lifecycle method

                    dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                    if (f.mView ! = null) {
                        f.restoreViewState(f.mSavedFragmentState);
                    }
                    f.mSavedFragmentState = null;
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState > Fragment.ACTIVITY_CREATED) {
                    f.mState = Fragment.STOPPED;
                }
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG."moveto STARTED: " + f);
                    f.performStart();
                    dispatchOnFragmentStarted(f, false);
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG."moveto RESUMED: " + f);
                    f.performResume();
                    dispatchOnFragmentResumed(f, false);
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null; }}}else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    if (DEBUG) Log.v(TAG."movefrom RESUMED: " + f);
                    f.performPause();
                    dispatchOnFragmentPaused(f, false);
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG."movefrom STARTED: " + f);
                    f.performStop();
                    dispatchOnFragmentStopped(f, false);
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG."movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    if (DEBUG) Log.v(TAG."movefrom ACTIVITY_CREATED: " + f);
                    if (f.mView ! = null) {
                        // Need to save the current view state if not
                        // done already.
                        if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState = = null) {
                            saveFragmentViewState(f);
                        }
                    }
                    f.performDestroyView();
                    dispatchOnFragmentViewDestroyed(f, false);
                    if (f.mView ! = null && f.mContainer ! = null) {
                        Animation anim = null;
                        if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                && f.mView.getVisibility() = = View.VISIBLE
                                && f.mPostponedAlpha > = 0) {
                            anim = loadAnimation(f, transit, false,
                                    transitionStyle);
                        }
                        f.mPostponedAlpha = 0;
                        if (anim ! = null) {
                            final Fragment fragment = f;
                            f.setAnimatingAway(f.mView);
                            f.setStateAfterAnimating(newState);
                            final View viewToAnimate = f.mView;
                            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                    viewToAnimate, anim) {
                                @Override
                                public void onAnimationEnd(Animation animation) {
                                    super.onAnimationEnd(animation);
                                    if (fragment.getAnimatingAway() ! = null) {
                                        fragment.setAnimatingAway(null);
                                        moveToState(fragment, fragment.getStateAfterAnimating(),
                                                0.0.false); }}}); f.mView.startAnimation(anim);
                        }
                        f.mContainer.removeView(f.mView);
                    }
                    f.mContainer = null;
                    f.mView = null;
                    f.mInnerView = null;
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    if (mDestroyed) {
                        if (f.getAnimatingAway() ! = null) {
                            // The fragment's containing activity is
                            // being destroyed, but this fragment is
                            // currently animating away. Stop the
                            // animation right now -- it is not needed,
                            // and we can't wait any more on destroying
                            // the fragment.
                            View v = f.getAnimatingAway();
                            f.setAnimatingAway(null);
                            v.clearAnimation(); }}if (f.getAnimatingAway() ! = null) {
                        // We are waiting for the fragment's view to finish
                        // animating away. Just make a note of the state
                        // the fragment now should move to once the animation
                        // is done.
                        f.setStateAfterAnimating(newState);
                        newState = Fragment.CREATED;
                    } else {
                        if (DEBUG) Log.v(TAG."movefrom CREATED: " + f);
                        if (!f.mRetaining) {
                            f.performDestroy();
                            dispatchOnFragmentDestroyed(f, false);
                        } else {
                            f.mState = Fragment.INITIALIZING;
                        }

                        f.performDetach();
                        dispatchOnFragmentDetached(f, false);
                        if (!keepActive) {
                            if (!f.mRetaining) {
                                makeInactive(f);
                            } else {
                                f.mHost = null;
                                f.mParentFragment = null;
                                f.mFragmentManager = null;
                            }
                        }
                    }
                }
        }
    }

    if (f.mState ! = newState) {
        Log.w(TAG."moveToState: Fragment state for " + f + " not updated inline; "
                + "expected state " + newState + " found " + f.mState);
        f.mState =newState; }}Copy the code

The code is long, but what it does is simple:

  1. Invoke the corresponding lifecycle method based on the state
  2. If it is newly created, add the layout to the ViewGroup

What is the fragments

Fragment: what Fragment is, from the official website, other people’s blogs are seen by others, we still have to look at the source code to get the answer.

public class Fragment implements ComponentCallbacks.OnCreateContextMenuListener {. }Copy the code

As you can see, Fragment doesn’t inherit any classes. It just implements these two interfaces. The second is less important, the first is that it can receive callbacks when it is out of memory.

No special information, but let’s go to its main members.

The main member of Fragment


static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

// The current Fragment state value
int mState = INITIALIZING;
/ /...
// True if the fragment is in the list of added fragments.
boolean mAdded;

// If set this fragment is being removed from its activity.
boolean mRemoving;

// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;

// Set to true when the view has actually been inflated in its layout.
boolean mInLayout;

// True if this fragment has been restored from previously saved state.
boolean mRestored;

// Number of active back stack entries this fragment is in.
int mBackStackNesting;

// Set to true when the app has requested that this fragment be hidden
// from the user.
boolean mHidden;

// Set to true when the app has requested that this fragment be deactivated.
boolean mDetached;

// If set this fragment would like its instance retained across
// configuration changes.
boolean mRetainInstance;

// If set this fragment is being retained across the current config change.
boolean mRetaining;

// If set this fragment has menu items to contribute.
boolean mHasMenu;

// Set to true to allow the fragment's menu to be shown.
boolean mMenuVisible = true;

// Used to verify that subclasses call through to super class.
boolean mCalled;Copy the code

A bunch of flag bits and status values. Then there are the key members:

// The fragment manager we are associated with. Set as soon as the
// fragment is used in a transaction; cleared after it has been removed
// from all transactions.
FragmentManagerImpl mFragmentManager;

//Fragmemt binds an object that is half an Activity or Fragment
FragmentHostCallback mHost;
// Manage child fragments
FragmentManagerImpl mChildFragmentManager;

// For use when restoring fragment state and descendant fragments are retained.
// This state is set by FragmentState.instantiate and cleared in onCreate.
FragmentManagerNonConfig mChildNonConfig;
// Set this value if the Fragment is bound to another Fragment
Fragment mParentFragment;
// ID of the container Fragment
int mFragmentId;
// The container View ID
int mContainerId;

/ / the parent layout
ViewGroup mContainer;

// The current Fragment layout
View mView;

// Truly save the internal layout of the state
View mInnerView;
Copy the code

A Fragment is created and added to a Fragment:

Return a layout in onCreateView(), grab the layout in FragmentManager, add it to the ViewGroup of the Activity/Fragment to be bound to, and set the corresponding state value.

Lifecycle approach

The Fragment life cycle is well known, and the official graph provides a very clear picture:

A total of 11 methods, here we look at the specific source of each method.

1. onAttach(Context)

@CallSuper
public void onAttach(Context context) {
    mCalled = true;
    final Activity hostActivity = mHost == null ? null : mHost.getActivity();
    if(hostActivity ! =null) {
        mCalled = false; onAttach(hostActivity); }}@Deprecated
@CallSuper
public void onAttach(Activity activity) {
    mCalled = true;
}Copy the code

OnAttach () is the first method called when a Fragment is associated with its Context, where we can get the corresponding Context or Activity, You can see that the Activity you get here is mhost. getActivity(), which we’ll introduce later when we introduce FragmentManager.

2. onCreate(Bundle)

public void onCreate(@Nullable Bundle savedInstanceState) {
    mCalled = true;
    restoreChildFragmentState(savedInstanceState);
    if(mChildFragmentManager ! =null&&! mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) { mChildFragmentManager.dispatchCreate(); }}void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {
    if(savedInstanceState ! =null) {
        Parcelable p = savedInstanceState.getParcelable(
                FragmentActivity.FRAGMENTS_TAG);
        if(p ! =null) {
            if (mChildFragmentManager == null) {
                instantiateChildFragmentManager();
            }
            mChildFragmentManager.restoreAllState(p, mChildNonConfig);
            mChildNonConfig = null; mChildFragmentManager.dispatchCreate(); }}}Copy the code

OnCreate () is called after onAttach() to do some initialization.

Note that the Fragment’s onCreate() call may have an Activity that has not yet been created, so there are no operations that depend on the external Activity layout. If you have activity-dependent operations, you can put them in onActivityCreate().

As you can also see from the code above, the sub-fragment state is restored if it is from an old state, and the creation of the sub-fragment manager is called in onCreate().

3. onCreateView(LayoutInflater, ViewGroup, Bundle)

@Nullable
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
    return null;
}Copy the code

OnCreatView () is executed after onCreate(), which returns a View, null by default.

When we need to display the layout in the Fragment, we need to override this method and return the layout to display.

OnDestroyView () will be called later when the layout is destroyed.

3.1. onViewCreated

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
}Copy the code

OnViewCreate () is not a lifecycle method, but it is useful.

It will be executed immediately after onCreateView() returns, and the view in the argument is the view created earlier, so we can initialize the layout in onViewCreate(), for example:

@Override
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
    if (view == null) {
        return;
    }
    mTextView = (TextView) view.findViewById(R.id.tv_content);
    mBtnSwitchChild = (Button) view.findViewById(R.id.btn_switch_child);

    Bundle arguments = getArguments();
    if(arguments ! =null&& mTextView ! =null && !TextUtils.isEmpty(arguments.getString(KEY_TITLE))) {
        mTextView.setText(arguments.getString(KEY_TITLE));
    }
    mBtnSwitchChild.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            / /...
    });
}Copy the code

4. onActivityCreated(Bundle)

@CallSuper
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    mCalled = true;
}Copy the code

OnActivityCreated () is called after the Activity associated with the Fragment has been created and the Fragment’s layout structure has been initialized.

You can do something about layout and state recovery in this method.

4.1 onViewStateRestored (Bundle)

@CallSuper
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    mCalled = true;
}Copy the code

The onViewStateRestored() method is called after onActivityCreated() and is used by a Fragment to retrieve the saveInstanceState state when it recovers from an old state. For example, restore a check box.

After these four steps, the Fragment is created and synchronized with the Activity creation process.

5. onStart()

@CallSuper
public void onStart() {
    mCalled = true;

    if(! mLoadersStarted) { mLoadersStarted =true;
        if(! mCheckedForLoaderManager) { mCheckedForLoaderManager =true;
            mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
        }
        if(mLoaderManager ! =null) { mLoaderManager.doStart(); }}}Copy the code

OnStart () is called when fragments are visible, synchronized with the Activity’s onStart().

6. onResume()

@CallSuper
public void onResume() {
    mCalled = true;
}Copy the code

OnResume () is called when the Fragment is visible and can interact with the user.

It synchronizes with the Activity’s onResume().

7. onPause()

@CallSuper
public void onPause() {
    mCalled = true;
}Copy the code

OnPause () is called when the Fragment is no longer visible.

Also synchronizes with the Activity’s onPause().

8. onStop()

@CallSuper
public void onStop() {
    mCalled = true;
}Copy the code

OnStop () is called when the Fragment is no longer started, the same as activity.onstop ().

9. onDestroyView()

@CallSuper
public void onDestroyView() {
    mCalled = true;
}Copy the code

OnDestroyView () is called when the layout (null or not) returned by onCreateView() is unbound from the Fragment.

The next time the Fragment is displayed, the layout is recreated.

10. onDestroy()

@CallSuper
public void onDestroy() {
    mCalled = true;
    //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
    // + " mLoaderManager=" + mLoaderManager);
    if(! mCheckedForLoaderManager) { mCheckedForLoaderManager =true;
        mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
    }
    if(mLoaderManager ! =null) { mLoaderManager.doDestroy(); }}Copy the code

OnDestroy () is called when the Fragment is no longer in use, which is the penultimate step in the Fragment lifecycle.

As you can see here, mloaderManager.dodestroy () is called, which is described later.

11. onDetach()

@CallSuper
public void onDetach() {
    mCalled = true;
}Copy the code

The last method in the Fragment life cycle, called when the Fragment is no longer bound to an Activity.

Fragment’s onDestroyView(), onDestroy(), and onDetach() correspond to the Activity’s onDestroyed() methods.

conclusion

OK, after reading this article, I believe you have the answer to the question raised at the beginning. Here is a summary.

Fragment, FragmentManager, or FragmentTransaction relationship

  • Fragment

    • It encapsulates the View, it holds the View, containerView, fragmentManager, childFragmentManager, etc
  • FragmentManager

    • Fragment is an abstract class that defines methods for managing the list of fragments that are added to an Activity/Fragment, and how to unstack them
    • Methods to get transaction objects are also defined
    • The implementation is in FragmentImpl
  • FragmentTransaction

    • Operations to add, replace, and hide fragments are defined, as well as four commit methods
    • The concrete implementation is in BackStackRecord

How does Fragment add and replace layouts

By getting the Activity/fragments FragmentManager/ChildFragmentManager, thus to get the transaction implementation class BackStackRecord, It constructs the target Fragment as an Ops (wrapping Fragment and state information) and submits it to the FragmentManager for processing.

If the commit is asynchronous, the FragmentManager sends the Runnable task to the Handler. When the FragmentManager receives the task, it handles the Ops state first, and then calls moveToState() to call the Fragment’s lifecycle method based on the state. In this way, fragments can be added, layouts can be replaced and hidden.

Here’s how a Fragment creates an experience from the bottom up:

The principle of nested Fragments

The Fragment has a childFragmentManager inside it that manages the child fragments.

When adding a child Fragment, add the child Fragment’s layout to the parent Fragment.