AndroidX based source analysis

The relationship between activities and Fragments

  • Fragments are part of the Activity interface and are attached to the Activity.
  • Activity is the basis of Fragment, Fragment is the continuation and development of Activity
  • An Activity can have multiple fragments, and a Fragment can be reused by multiple activities.
  • A Fragment can flexibly control its own life cycle except when the Activity is in the onResume state. In other states, its life cycle is determined by the Activity.
  • Fragment increases the flexibility of UI layout and can change UI layout dynamically.

Fragment life cycle

This is a picture of the Fragment life cycle provided by Google. The Fragment life cycle depends on activities

We start our analysis of the source code with the above figure

1. Create FragmentController

There is a variable mFragments in the FragmentActivity that is initialized at the beginning

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
Copy the code

The entire structure of the class diagram is shown above, and the details will be covered in the subsequent flow.

Second, life cycle distribution

Our Fragment’s life cycle changes with the Activity’s life cycle, so let’s look at a few lifecycle calls.

protected void onCreate(@Nullable Bundle savedInstanceState) {
  	/ / call the attach
    mFragments.attachHost(null /*parent*/);

    if(savedInstanceState ! =null) {
        / /... Data retention correlation
    }
		
  	/ /...
    super.onCreate(savedInstanceState);
  	/ / distribute the create
    mFragments.dispatchCreate();
}

protected void onPostResume(a) {
    super.onPostResume();
  	// Resume is distributed, which is executed before onResume
    mFragments.dispatchResume();
}

protected void onPause(a) {
    super.onPause();
    mResumed = false;
  	/ / distribute Pause
    mFragments.dispatchPause();
}
Copy the code

The above code can be seen as the change of the Activity lifecycle will call FragmentManagerImpl. DispatchXXX (). Tracking code. We’ll find FragmentManagerImpl dispatchXXX () in the end will be called FragmentManagerImpl. DispatchStateChange (int nextState)

Fragment state

Fragment has multiple state values that indicate the current state of its life cycle

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // Fully created, not started.
static final int STARTED = 3;          // Created and started, not resumed.
static final int RESUMED = 4;          // Created started and resumed.
// The default Fragment state is INITIALIZING, which represents the current life cycle state
int mState = INITIALIZING;
Copy the code

FragmentManagerImpl.dispatchStateChange

//nextState, as the name implies, is the nextState you want your Fragment to enter
private void dispatchStateChange(int nextState) {
    try {
      	// This flag bit is used to prevent
        mExecutingActions = true;
      	//1. Change to the next state
        moveToState(nextState, false);
    } finally {
        mExecutingActions = false;
    }
  	// Perform unfinished operations
    execPendingActions();
}
Copy the code

Iii. Life cycle changes moveToState

Step 1

void moveToState(int newState, boolean always) {
    if (mHost == null&& newState ! = Fragment.INITIALIZING) {throw new IllegalStateException("No activity");
    }
		// If the status is the same, return
    if(! always && newState == mCurState) {return;
    }
		
    mCurState = newState;

    // Must add them in the proper order. mActive fragments may be out of order
    final int numAdded = mAdded.size();
    for (int i = 0; i < numAdded; i++) {
        Fragment f = mAdded.get(i);
      	// All fragments will call this method, but will not actually update the state.
        moveFragmentToExpectedState(f);
    }

    // Now iterate through all active fragments. These will include those that are removed
    // and detached.
  	// Special cases such as remove and detached that are recycled will not be added to the mAdded list
    for (Fragment f : mActive.values()) {
        if(f ! =null&& (f.mRemoving || f.mDetached) && ! f.mIsNewlyAdded) {// [1-2] The lifecycle changes of recycled re-executionmoveFragmentToExpectedState(f); }}// For example, some fragments need to wait for other fragments to finish executing before executing their own life-cycle changes. For example, if the fragments are not visible to the user, they need to execute the life-cycle first before executing their own life-cycle changes. This is also Google's idea of optimization, so when to delay Todo
  	// [1-3] Start executing delayed Fragment lifecycle changes [3-1]
    startPendingDeferredFragments();
		/ /...
}
Copy the code

[1-3] startPendingDeferredFragments = > performPendingDeferredStart

public void performPendingDeferredStart(Fragment f) {
    if (f.mDeferStart) {
        if (mExecutingActions) {
            mHavePendingDeferredStart = true;
            return;
        }
      	// Clear the lazy load flag
        f.mDeferStart = false;
      	// Then go to step 3 of the life cycle
        moveToState(f, mCurState, 0.0.false); }}Copy the code

Step 2

void moveFragmentToExpectedState(Fragment f) {
    if (f == null) {
        return;
    }
  	// [2-1] The mActive can be executed only when there is a key
    if(! mActive.containsKey(f.mWho)) {return;
    }
    int nextState = mCurState;
    if (f.mRemoving) {
      	// If removed from control his life cycle nextState cannot exceed expectations
        if (f.isInBackStack()) {
            nextState = Math.min(nextState, Fragment.CREATED);
        } else{ nextState = Math.min(nextState, Fragment.INITIALIZING); }}// [2-2] get into the real state
    moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
		/ /...
}
Copy the code

Step 3

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
                 boolean keepActive) {
 		// Unadded or detached go oncreate first
    if((! f.mAdded || f.mDetached) && newState > Fragment.CREATED) { newState = Fragment.CREATED; }// In this control newState if it is removed from control its life cycle nextState cannot exceed expectations
    if (f.mRemoving && newState > f.mState) {
        if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {
            // Allow the fragment to be created so that it can be saved later.
            newState = Fragment.CREATED;
        } else {
            // While removing a fragment, we can't change it to a higher state.newState = f.mState; }}// Lazy loading assigns newState directly to fragment.activity_created, which will not execute CREATED and INITIALIZING and skip the life cycle
    if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.ACTIVITY_CREATED) {
        newState = Fragment.ACTIVITY_CREATED;
    }
  	ViewPager2 is used to control which step in the Fragment lifecycle is executed
    if (f.mMaxState == Lifecycle.State.CREATED) {
        newState = Math.min(newState, Fragment.CREATED);
    } else {
        newState = Math.min(newState, f.mMaxState.ordinal());
    }
    if (f.mState <= newState) {
				/ /...
      	// Switch current status
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (newState > Fragment.INITIALIZING) {// Execute the instructions to go to the CREATE lifecycle state
                    if(f.mSavedFragmentState ! =null) {
                        / /... back
                      	//【 1-3】 If the Fragment user is not visible, do not let him go to START until all the execution is complete.
                        if(! f.mUserVisibleHint) { f.mDeferStart =true;
                            if(newState > Fragment.ACTIVITY_CREATED) { newState = Fragment.ACTIVITY_CREATED; } } } f.mHost = mHost; f.mParentFragment = mParent; f.mFragmentManager = mParent ! =null
                            ? mParent.mChildFragmentManager : mHost.mFragmentManager;

                    // Execute the ParentFragment dependent on Childfragment first
                    if(f.mTarget ! =null) {
                        if (f.mTarget.mState < Fragment.CREATED) {
                            moveToState(f.mTarget, Fragment.CREATED, 0.0.true);
                        }
                        f.mTargetWho = f.mTarget.mWho;
                        f.mTarget = null;
                    }
                    if(f.mTargetWho ! =null) {
                        Fragment target = mActive.get(f.mTargetWho);
                        if (target.mState < Fragment.CREATED) {
                            moveToState(target, Fragment.CREATED, 0.0.true); }}// Lifecycle callback and distribution
                    dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                  	//fragment attach
                    f.performAttach();
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    } else {
                        f.mParentFragment.onAttachFragment(f);
                    }
                    dispatchOnFragmentAttached(f, mHost.getContext(), false);
										//f.mState is marked as CREATED
                    if(! f.mIsCreated) { dispatchOnFragmentPreCreated(f, f.mSavedFragmentState,false);
                        f.performCreate(f.mSavedFragmentState);
                        dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                    } else{ f.restoreChildFragmentState(f.mSavedFragmentState); f.mState = Fragment.CREATED; }}case Fragment.CREATED:
                if (newState > Fragment.INITIALIZING) {
                  	/ / create FragmentView
                    ensureInflatedFragmentView(f);
                }

                if (newState > Fragment.CREATED) {// Execute the instructions inside to go to the ACTIVITY_CREATE lifecycle state
                    if(! f.mFromLayout) {/ /... Add the Fragment View to the Activity View container
                    }
										//f.mState is marked as ACTIVITY_CREATED
                    f.performActivityCreated(f.mSavedFragmentState);
                    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) {// Execute this instruction to go to the START life cycle state
                    f.performStart();
                    dispatchOnFragmentStarted(f, false);
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {// Go to the RESUME lifecycle state
                  	// Execute Fragment Resume
                    f.performResume();
                  	// The callback that the distribution lifecycle listens on
                    dispatchOnFragmentResumed(f, false);
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null; }}}else if (f.mState > newState) {
        INITIALIZING = INITIALIZING = INITIALIZING = INITIALIZING = INITIALIZING = INITIALIZING = INITIALIZING
    }
		// Set the current state of the fragment
    if (f.mState != newState) {
        f.mState = newState;
    }
}
Copy the code

Transaction management

There are two ways to add a Fragment to an Activity layout.

  • Join using the Fragment tag (static join)
  • Dynamic join through code (dynamic join)

And this is the second way.

The transaction of the action

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;
static final int OP_SET_PRIMARY_NAV = 8;
static final int OP_UNSET_PRIMARY_NAV = 9;
static final int OP_SET_MAX_LIFECYCLE = 10;
Copy the code

The process of adding transactions

The differences between the four types of Commit for transactions

www.cnblogs.com/mengdd/p/58…

Pass data between fragments

To reuse Fragment interface components, you should build each component into a completely separate modular component, defining its own layout and behavior. Once these reusable fragments are defined, you can associate them with activities and with application logic to achieve an overall composite interface.

Starting with AndroidX Fragment version 1.3.0-Alpha04, every FragmentManager implements FragmentResultOwner. We implement communication between fragments.

  • Data transfer between two fragments of the same level, bidirectional transmission
  • The ChildFragment sends data to the ParentFragment. ChildFragment => ParentFragment
  • Jetpack’s ViewModel communicates with the LiveData component, and by sharing the ViewModel, two-way communication of data can be achieved using LiveData. This legacy of communication will be covered later in the MVVM architecture.

We can see that the above communication condition has to be under the same Activity, and if it’s across Acitivty that’s a lifetime longer than Acitivity looking at design patterns globally, RxJava, EventBus, LiveDataBus and other mature thread communication open source components.

State saving and recovery

State of preservation

When the Activity is the process of recycling or App in the background in the Sleep state while waiting for special circumstances invoked ActivityThread. CallActivityOnSaveInstanceState = > Instrumentation.callActivityOnSaveInstanceState => Activity.performSaveInstanceState => Activity.onSaveInstanceState => FragmentActivity onSaveInstanceState to save the data

FragmentActivity.onSaveInstanceState

protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
  	// Change the Fragment state to CREATE
    markFragmentsCreated();
    mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
  	//1. Encapsulate all state data to be saved in the Fragment
    Parcelable p = mFragments.saveAllState();
    if(p ! =null) {
      	//2. Save the data to the bundle
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    if (mPendingFragmentActivityResults.size() > 0) {
        outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

        int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
        String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
        for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) { requestCodes[i] = mPendingFragmentActivityResults.keyAt(i); fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i); } outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes); outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos); }}Copy the code

After first step method performs mFragments. SaveAllState = > FragmentController. SaveAllState = > FragmentManagerImpl. SaveAllState

The total enter FragmentManagerImpl saveAllState

FragmentManagerImpl.saveAllState

Parcelable saveAllState(a) {
  	// Make sure all waiting operations have been processed before entering save data
    forcePostponedTransactions();
    endAnimatingAwayFragments();
    execPendingActions();
  
		//1. Commit throws an exception when mStateSaved == true
    mStateSaved = true;

  	// Fragment is empty, i.e. no fragment is added to FragmentManager
    if (mActive.isEmpty()) {
        return null;
    }

    // First collect all active fragments.
    int size = mActive.size();
    ArrayList<FragmentState> active = new ArrayList<>(size);
    boolean haveFragments = false;
  	// Iterate through all active fragments
    for (Fragment f : mActive.values()) {
        if(f ! =null) {
            if(f.mFragmentManager ! =this) {
                throwException(new IllegalStateException(
                        "Failure saving state: active " + f
                                + " was removed from the FragmentManager"));
            }

            haveFragments = true;
						//2. Save the fragment member variables and add them to the Active component for data recovery
            FragmentState fs = new FragmentState(f);
            active.add(fs);

            if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
              	// Save the base state
                fs.mSavedFragmentState = saveFragmentBasicState(f);
								
                if(f.mTargetWho ! =null) {
                    Fragment target = mActive.get(f.mTargetWho);
                    if (fs.mSavedFragmentState == null) {
                        fs.mSavedFragmentState = new Bundle();
                    }
                    putFragment(fs.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG, target);
                    if(f.mTargetRequestCode ! =0) { fs.mSavedFragmentState.putInt( FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, f.mTargetRequestCode); }}}else{ fs.mSavedFragmentState = f.mSavedFragmentState; }}}if(! haveFragments) {return null;
    }

    ArrayList<String> added = null;
    BackStackState[] backStack = null;

    // Build list of currently added fragments.
    size = mAdded.size();
    if (size > 0) {
      	// Iterate over all fragments added to FragmentManager
        added = new ArrayList<>(size);
        for (Fragment f : mAdded) {
          	//f.mWho fragmentadded.add(f.mWho); }}// Save the rollback
    // Now save back stack.
    if(mBackStack ! =null) {
        size = mBackStack.size();
        if (size > 0) {
            backStack = new BackStackState[size];
            for (int i = 0; i < size; i++) {
                backStack[i] = newBackStackState(mBackStack.get(i)); }}}/ / 3. Saves the FragmentManagerImpl as a whole
    FragmentManagerState fms = new FragmentManagerState();
    fms.mActive = active;
    fms.mAdded = added;
    fms.mBackStack = backStack;
    if(mPrimaryNav ! =null) {
        fms.mPrimaryNavActiveWho = mPrimaryNav.mWho;
    }
    fms.mNextFragmentIndex = mNextFragmentIndex;
    return fms;
}
Copy the code

The steps above finally save our data to the ActivityClienttRecord member variable State

back

When the Activity resumes, a new Activity is created and the Activity lifecycle method is traversed.

FragmentActivity.onCreate

  1. = >FragmentController.restoreSaveState= >FragmentManagerImpl.restoreSaveStateRestore data
  2. = >FragmentController.dispatchCreate= >FragmentManagerImpl.dispatchCreateRetrace the Fragment life cycle

FragmentManagerImpl.restoreSaveState

void restoreSaveState(Parcelable state) {
    // If there is no saved state at all, then there's nothing else to do
    if (state == null) return;
    FragmentManagerState fms = (FragmentManagerState)state;
    if (fms.mActive == null) return;

    // First re-attach any non-config instances we are retaining back
    // to their saved state, so we don't try to instantiate them again.
    for (Fragment f : mNonConfig.getRetainedFragments()) {
        FragmentState fs = null;
        for (FragmentState fragmentState : fms.mActive) {
            if (fragmentState.mWho.equals(f.mWho)) {
                fs = fragmentState;
                break; }}if (fs == null) {
            // We need to ensure that onDestroy and any other clean up is done
            // so move the Fragment up to CREATED, then mark it as being removed, then
            // destroy it.
            moveToState(f, Fragment.CREATED, 0.0.false);
            f.mRemoving = true;
            moveToState(f, Fragment.INITIALIZING, 0.0.false);
            continue;
        }
        fs.mInstance = f;
        f.mSavedViewState = null;
        f.mBackStackNesting = 0;
        f.mInLayout = false;
        f.mAdded = false; f.mTargetWho = f.mTarget ! =null ? f.mTarget.mWho : null;
        f.mTarget = null;
        if(fs.mSavedFragmentState ! =null) { fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader()); f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( FragmentManagerImpl.VIEW_STATE_TAG); f.mSavedFragmentState = fs.mSavedFragmentState; }}// Empty active fragments first
    mActive.clear();
  	// Remove active fragments from the cache and add them to mActive
    for (FragmentState fs : fms.mActive) {
        if(fs ! =null) {
            Fragment f = fs.instantiate(mHost.getContext().getClassLoader(),
                    getFragmentFactory());
            f.mFragmentManager = this;
            if (DEBUG) Log.v(TAG, "restoreSaveState: active (" + f.mWho + ")." + f);
            mActive.put(f.mWho, f);
            fs.mInstance = null; }}// Build the list of currently added fragments.
  	// Restore the Fragment of add in the same way
    mAdded.clear();
    if(fms.mAdded ! =null) {
        for (String who : fms.mAdded) {
            Fragment f = mActive.get(who);
            if (f == null) {
                throwException(new IllegalStateException(
                        "No instantiated fragment for (" + who + ")"));
            }
            f.mAdded = true;
            if (mAdded.contains(f)) {
                throw new IllegalStateException("Already added " + f);
            }
            synchronized(mAdded) { mAdded.add(f); }}}// Build the back stack.
    if(fms.mBackStack ! =null) {
        mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
        for (int i=0; i<fms.mBackStack.length; i++) {
            BackStackRecord bse = fms.mBackStack[i].instantiate(this);
            mBackStack.add(bse);
            if (bse.mIndex >= 0) { setBackStackIndex(bse.mIndex, bse); }}}else {
        mBackStack = null;
    }

    if(fms.mPrimaryNavActiveWho ! =null) {
        mPrimaryNav = mActive.get(fms.mPrimaryNavActiveWho);
        dispatchParentPrimaryNavigationFragmentChanged(mPrimaryNav);
    }
    this.mNextFragmentIndex = fms.mNextFragmentIndex;
}
Copy the code