The source code in this article is based on the Support package version 27.1.1
1 Fragment Life cycle
Everyone knows about the Fragment lifecycle and its corresponding lifecycle functions:
Fragment
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.
Copy the code
The Fragment’s entire life cycle flows through these six states, calling the corresponding lifecycle method and then entering the next state, as shown in the figure below
1.1 fragments and Activity
The Fragment lifecycle is closely related to the Activity lifecycle. An Activity manages the Fragment lifecycle by calling the corresponding method of the FragmentManager in the Activity lifecycle method, Migrate existing fragments to the next state using the FragmentManager and fire the appropriate lifecycle functions
Activity life cycle function | Function triggered by FragmentManager | Fragment state migration | Fragment life cycle callback |
---|---|---|---|
onCreate | dispatchCreate | INITIALIZING-> CREATED |
OnAttach, onCreate |
onStart | dispatchStart | CREATED-> ACTIVITY_CREATED-> STOPPED-> STARTED |
OnCreateView, onActivityCreated, onStart |
OnResume (to be precise, onPostResume) | dispatchResume | STARTED-> RESUMED |
onResume |
onPause | dispatchPause | RESUMED-> STARTED |
onPause |
onStop | dispatchStop | STARTED-> STOPPED |
onStop |
onDestroy | dispatchDestroy | STOPPED-> ACTIVITY_CREATED-> CREATED-> INITIALIZING |
OnDestroyView, onDestroy, onDetach |
The previous image is even clearer:
1.2 with FragmentTransaction fragments
We often use FragmentTransaction methods like Add, remove, replace, attach, detach, hide, and show to operate on a Fragment. These methods change the state of the Fragment and trigger the corresponding lifecycle function
(Assuming the Activity is in RESUME state)
Methods in FragmentTransaction | Life cycle function triggered by Fragment |
---|---|
add | onAttach-> onCreate-> onCreateView-> onActivityCreated-> onStart-> onResume |
remove | onPause-> onStop-> onDestoryView-> onDestory-> onDetach |
replace | Replace can be split into add and remove, |
detach | (You need to add the Fragment before calling detach) onPause-> onStop-> onDestoryView |
attach | (You need to call detach before calling Attach) onCreateView-> onActivityCreated-> onStarted-> onResumed |
hide | No lifecycle functions are triggered |
show | No lifecycle functions are triggered |
By observing changes in the Fragment life cycle, we can easily see that add/remove causes the Fragment to migrate between INITIALIZING and RESUMED states. The attach/detach operation causes the Fragment to migrate between the CREATED and RESUMED states.
Note: One important thing to note about the add function is that if your Fragment is in the STARTED state, it can’t go to any state until it’s RESUMED. Then trigger onResume – > FragmentManager. DispatchStateChange (fragments. RESUMED), then calls the fragments. After onResume function fragments into a state will be RESUMED.
1.3 fragments with the ViewPager
With FragmentPagerAdapter we can combine fragments with ViewPager. What is the life cycle of a Fragment in a ViewPager?
The FragmentPagerAdapter operates on the Fragment using FragmentTransaction, including add, detach, and attach.
@SuppressWarnings("ReferenceEquality")
@Override
public Object instantiateItem(ViewGroup container, int position) {
/ /...
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if(fragment ! =null) {
// If there is an existing Fragment instance
// Add by attach
mCurTransaction.attach(fragment);
} else {
// The Fragment instance has not been created yet. Create an instance using getItem
// Then add with the add operation
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
/ /...
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
/ /...
// Use detach to destroy the Fragment
mCurTransaction.detach((Fragment)object);
}
Copy the code
As you can see from the source code above, the FragmentPagerAdapter adds fragments through the FragmentTransaction.add method, and then by attaching and detach. The corresponding life cycle of these methods can be referred to the figure above. Let’s simulate this for example. Suppose we have ViewPager with 5 pages, and offscreenPageLimit is 1,
- The first time it loads, the first and second pages pass
add
The function is loaded atRESUMED
state - Slide to the second page and the third page is loaded, also through
add
The function is loaded atRESUMED
state - Continue to slide to page 3, where the first page passes
detach
The function is recycled atCREATED
Status, while page 4 passesadd
Be loaded inRESUMED
state - Slide to the second page, when the first page passes
attach
Loaded, inRESUMED
Status, page four bydetach
In aCREATED
state
Summary: In the ViewPager, the current page and the left and right pages of the current page are all in the RESUMED state. Other pages are either not CREATED or are CREATED. The Fragment lifecycle changes during a slide can be seen in the example above.
1.4 with DialogFragment fragments
When using DialogFragment, we are used to using its show and hide methods to display or hide. Internally, these two methods use the Add and remove methods of FragmentTransaction, whose lifecycle we’ve already covered.
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
// Core operations
ft.add(this, tag);
ft.commit();
}
void dismissInternal(boolean allowStateLoss) {
/ /...
if (mBackStackId >= 0) {
/ /...
} else {
FragmentTransaction ft = getFragmentManager().beginTransaction();
// Core operations
ft.remove(this);
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else{ ft.commit(); }}}Copy the code
DialogFragment is special in that it maintains a Dialog internally. At the beginning of DialogFragment design, it uses FragmentManager to manage Dialog, mainly using three methods of Dialog: show, hide, and dismiss. The corresponding relationship is as follows
Fragment life cycle function | Method of the corresponding Dialog |
---|---|
onStart | show |
onStop | hide |
onDestoryView | dismiss |
2 What are the effects of different methods of adding fragments on their life cycle
Fragment can be added in two ways:
- Add it by using the fragment tag in the XML file
- Use in code
FragmentTransaction
add
Let’s talk about how these two different additions can affect the Fragment’s lifecycle callbacks.
2.1 Using the Fragment Label to Add data
The creation of Fragment instances in XML is ultimately left to the FragmentManager with the onCreateView method
//FragmentManager.java
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// Check whether it is a Fragment label
if (!"fragment".equals(name)) {
return null;
}
// The following code is defined in the fetch XML
//Fragment some information
// Such as class name (full path), ID, tag
String fname = attrs.getAttributeValue(null."class");
TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
if (fname == null) {
fname = a.getString(FragmentTag.Fragment_name);
}
int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
String tag = a.getString(FragmentTag.Fragment_tag);
a.recycle();
// Check whether the specified Fragment class is derived from a Fragment
if(! Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {return null;
}
// The ID of the Container containing the Fragment must be not empty, the tag is not empty, or the ID of the Container containing the Fragment is not empty
// Otherwise an exception is thrown
intcontainerId = parent ! =null ? parent.getId() : 0;
if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
throw new IllegalArgumentException(attrs.getPositionDescription()
+ ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
}
// If we restored from a previous state, we may already have
// instantiated this fragment from the state and should use
// that instance instead of making a new one.Fragment fragment = id ! = View.NO_ID ? findFragmentById(id) :null;
if (fragment == null&& tag ! =null) {
fragment = findFragmentByTag(tag);
}
if (fragment == null&& containerId ! = View.NO_ID) { fragment = findFragmentById(containerId); }//log...
// Create a Fragment instance with reflection
if (fragment == null) {
fragment = Fragment.instantiate(context, fname);
// This field marks that the Fragment instance is from an XML file
fragment.mFromLayout = true; fragment.mFragmentId = id ! =0 ? id : containerId;
fragment.mContainerId = containerId;
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
fragment.mHost = mHost;
fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
// Focus on methods
// The second parameter is called moveToStateNow
// This is true, so the Fragment will be immediate
// Migrate to the current state recorded by FragmentManager
// Usually we set layout in the onCreate method
// So in general this is the FragmentManager
// CREATED state
addFragment(fragment, true);
} else if (fragment.mInLayout) {
/ /...
} else {
/ /...
}
if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
// If FragmentManager is currently in INITIALIZING state
// Force the Fragment to be migrated to the CREATED state
moveToState(fragment, Fragment.CREATED, 0.0.false);
} else {
// If the FragmentManager state is greater than CREATED
// Then migrate the Fragment to the corresponding state
moveToState(fragment);
}
/ /...
return fragment.mView;
}
Copy the code
The job of onCreateView is basically to create the Fragment instance and migrate it to the specified state. We use a process that normally starts an Activity as a scenario for analysis, and the Fragment will eventually enter the CREATED state.
When we looked at the Fragment life cycle, we mentioned that when an Activity enters onCreate, it triggers the Fragment’s onAttach and onCreate life cycle callback. In this case, however, the Fragment will trigger onCreateView to create the view in advance, as can be seen in moveToState’s source code:
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
/ /...
switch (f.mState) {
case Fragment.INITIALIZING:
/ /...
case Fragment.CREATED:
/ /...
/ / the following if statement from ensureInflatedFragmentView method
// The method code is pasted here for convenience
// If the Fragment comes from a layout file
// Then trigger onCreateView to create the attempted instance
if(f.mFromLayout && ! f.mPerformedCreateView) { f.mView = f.performCreateView(f.performGetLayoutInflater( f.mSavedFragmentState),null, f.mSavedFragmentState);
if(f.mView ! =null) {
f.mInnerView = f.mView;
f.mView.setSaveFromParentEnabled(false);
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
} else {
f.mInnerView = null; }}if (newState > Fragment.CREATED) {
/ /...
}
/ /...
}
/ /...
}
Copy the code
2.2 Add FragmentTransaction in your code
Here we use adding a Fragment in the activity. onCreate method as the analysis scenario
public class DemoActivity extends FragmentActivity{
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.demo);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.container, newDemoFragment()); ft.commit(); }}Copy the code
Regardless of what happens in add, we know that if we don’t call commit, add won’t work. Commit method will experience the following invocation chain commit – > commitInternal – > FragmentManager. EnqueueAction
// The implementation class for FragmentTransaction is BackStackRecord
// The actual type of action is BackStackRecord
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if(! allowStateLoss) { checkStateLoss(); }synchronized (this) {
/ /...
mPendingActions.add(action);
synchronized (this) {
booleanpostponeReady = mPostponedTransactions ! =null && !mPostponedTransactions.isEmpty();
booleanpendingReady = mPendingActions ! =null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
/ / the key
//getHandler gets a main thread Handler
// Instead of calling moveToState directly, one is thrown
// Message to message queue, which will cause the Fragment state migration to be delayedmHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); }}}}Copy the code
When triggered mExecCommit will experience the following invocation chain FragmentManager. ExecPendingActions – > BackStackRecord. GenerateOps – >… – > BackStackRecord. ExecuteOps – > FragmentManager. XxxFragment – > FragmentManager. MoveToState fragments happened eventually state transition
Does mExecCommit really just sit in the message queue waiting to be executed? The answer is no. Let’s look at the FragmentActivity.onStart method
protected void onStart(a) {
super.onStart();
/ /...
/ / on the blackboard
mFragments.execPendingActions();
/ /...
mFragments.dispatchStart();
/ /...
}
Copy the code
As you can see, execPendingActions are triggered early, and then dispatchStart, The Fragment will be migrated from INITIALIZING to STARTED(the execPendingActions method will remove the mExecCommit from the message queue when triggered). FragmentActivity in onStart, onResume and onPostResume lifecycle callback will be called FragmentManager. ExecPendingActions, So when we add fragments to our code in activity. onStart and activity. onResume, the Fragment state transitions will occur after activity. onResume and activity. onPostResume, respectively. So what happens when you add a Fragment after onPostResume? . At this time due to onPostResume method of FragmentManager execPendingActions had already in the super call, therefore mExecCommit will be triggered, One of the biggest differences here is that the Fragment life cycle changes in a different message cycle than the Activity life cycle.
2.3 summarize
Let’s conclude this section with a picture:
The STOPPED state was removed from the support package for version 28.0.0, but the life changes were consistent with the above figure when tested