I have been working for so many years, taking notes all the time, not Posting anything. I always feel that I already have it online, so I am too lazy to write it.


Finally want to think, or from the beginning of the most basic, gradually planing analysis of the principle, test their understanding of the degree, hope that all the great gods to discuss guidance, if there are mistakes welcome to correct, light spray!


By the way, the source code covered in this article is based on Android-28, and AndroidX


Return to the chase

Speaking of Fragments, we will definitely start with the life cycle. Let’s show the life cycle flow of Fragments with a classic official flow chart

A brief description of each life cycle

OnAttach (Context Context) : Called when the Fragment and Activity are attached, and only once. In this callback, we can convert the context to an Activity and save it, thus avoiding frequent later calls to getAtivity() to retrieve the Activity. This avoids an exception where getAtivity() is null (in the case of Activity and Fragment separation).

OnCreate: Called when the Fragment is originally created, similar to onCreate for an Activity.

OnCreateView: Called when you are preparing to draw the Fragment interface. Returns the root view of the Fragment to draw the layout. Alternatively, it can return null. Note that when building views with inflaters, attachToRoot must indicate false, because the Fragment will automatically add the View to the container. Attachtoroot being true will result in a duplicate error. OnCreateView is not always called, it is not called when adding a Fragment with no interface, such as calling the Add (Fragment Fragment, String Tag) method of FragmentTransaction.

OnActivityCreated: Called when onCreated of the Activity has finished executing.

OnStart: Fragment is called when the Activity is visible to the user, if the Activity is started.

OnResume: Fragment is called when the user can interact with it before, if the Activity has resumed.

OnPause: Called when the Fragment and the user are not previously interacting.

Called when onStop: Fragment is not visible.

OnDestroyView: Called when you remove the Fragment’s related view hierarchy.

OnDestroy: Called when the Fragment state is finally clear.

OnDetach: Called when a Fragment and Activity are disassociated.

Does that look familiar? That’s right! The life cycle of a fragment is similar to the life cycle of an activity. In fact, the life cycle of a fragment is related to the life cycle of an activity. Here is an official picture to illustrate it

Just to add, in the startup phase, onCreate, onStart, onResume, the activity lifecycle is called first in the Fragment phase, and in the pause shutdown phase, onPause, onStop, onDeforeStory, The Fragment lifecycle is tuned first before the activity is called. If the fragment is created later than the activity, the fragment will leak memory. If the fragment is created later than the activity, the fragment will leak memory.

1. What does the fragment do in each lifecycle? 2. How did the fragment rebuild? 3. How do activities and fragments interact? 4. How do fragments load lazily?

Let’s take a look at Fragments around these questions. (There are questions to be added after editing)

1. What does the fragment life cycle do?

This question is useless, go back to the top, it has been answered!

2. How did the fragment rebuild?

We know that in some cases, our Activity will be recycled and rebuilt, and a Fragment will be attached to the Activity, and of course it will be destroyed and rebuilt. The method of Activity scene reconstruction mainly relies on onSaveInstanceState to save the state of the page data, and load the saved data for reconstruction when rebuilding. The fragmentne? Is it the same as an Activity? Here are two common ways to rebuild fragments: 1. Use the setRetainInstance method of fragment to keep the fragment instance. Let’s check the official documentation

Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). If set, the fragment lifecycle will be slightly different when an activity is recreated:


{@link #onDestroy()} will not be called (but {@link #onDetach()} still will be, because the fragment is being detached from its current activity).


{@link #onCreate(Bundle)} will not be called since the fragment is not being re-created.


{@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} will still be called.

Controls whether the fragment is retained when the Activity is rebuilt. If this is set, the corresponding life cycle calls will be different.

When an Activity is destroyed, the Fragment will be preserved, and the remaining Fragment instance will be passed to the new Activity the next time the Activity is rebuilt. One important thing to note here is that this method is mainly used when the device configuration changes and the hosted Activity is being destroyed. Fragments will be kept for a short period of time, and the Fragment will be destroyed when the application is recovered due to insufficient memory. (It is not possible to keep a fragment instance to hold up memory, since memory needs to be destroyed.)

When the device configuration changes, the FragmentManager first destroys the view of the Fragment in the queue. Next, the FragmentManager checks the value of the RetainInstance property for each Fragment.

If mRetainInstance is true, the Fragment itself is preserved. When a new Activity is created, the new FragmentManager finds the preserved Fragment and recreates its view.

If mRetainInstance is false, the FragmentManager will destroy the Fragment instance directly, and when a new Activity is created, the new FragmentManager will create a new Fragment

By extension, since our Fragment instance is preserved, we can’t recreate the instance when the Activity is rebuilt, otherwise we will have two Fragment instances and a problem will occur. If the Fragment does not exist, create the Fragment with a Tag.

Method 2. Create a Fragment with a FragmentActivity

We know that when an Activity is destroyed, it calls onSaveInstanceState(Bundle OutState) to save its state. FragmentActivity inherits from the Activity. Let’s see what the onSaveInstanceState(Bundle outState) of FragmentActivity does

@Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); markFragmentsCreated(); mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP); // Returns the FragmentManagerState instance Parcelable p = MFragments.saveAllState(); if (p ! = null) { 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); }}

Parcelable p = MFragments. SaveAllState () : Parcelable p = MFragments. SaveAllState () : Parcelable p = MFragments. Don’t want to write too long, so don’t post the code

We see the save () method returns the Parcelable is actually FragmentManagerState instance, FragmentManagerState FragmentState contains members, FragmentState contains description data variables of the fragments, Enough to recreate a destroyed Fragment from zero

After saving the Fragment data, both the Activity instance and any Fragment instance that did not call setRetainInstance(true) are destroyed.

When an Activity is rebuilt, FragmentActivity recreates the Fragment that was previously saved and destroyed by calling the FragmentManager’s RestoreAllState method

This will complete the Fragment.

3. How do activities and fragments interact?

Here we break it up into sections, Activity calls Fragment, Fragment calls Activity, Fragment calls Fragment

1. The Activity call fragments

There are a number of ways to do this, from the initial pass-through of the Bundle, to calling methods of the Fragment from an instance of the Fragment

It is even possible to pass values directly through the Fragment’s constructor (not recommended here, since the Fragment is called with a no-argument constructor and the data passed may be lost).

So how do you do that? Without further ado, the last simple demo code

Public class DemoRagment extends Fragment {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () {public DemoRagment () DemoFragment getInstance(String data){ DemoFragment demoFragment = new DemoFragment(); Bundle bundle = new Bundle(); bundle.putString("data",data); demoFragment.setArguments(bundle); return demoFragment; }}

We recommend that when we initialize the pass, we create a method and the user receives the parameter, and then we create it and pass the value through the bundle.

2. Call the Activity fragments

There are a number of ways to do this, including using an interface to call back the value, using a strong pass to get the value of an Activity instance, or using a broadcast EventBus

Let’s focus on interface callback values. The other methods have some disadvantages. The code is not necessarily elegant

The main interface interaction is to define an interface in a Fragment that allows the host Activity to listen for a callback and upload the code

public class DemoFragment extends Fragment implements View.OnClickListener{ Button demoButton; Public static DemoFragment getInstance(String data){public static DemoFragment getInstance(String data){public static DemoFragment getInstance(String data){ DemoFragment demoFragment = new DemoFragment(); Bundle bundle = new Bundle(); bundle.putString("data",data); demoFragment.setArguments(bundle); return demoFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CallbackListener = (callbackListener) getActivity(); callbackListener = (callbackListener); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view =inflater.inflate(R.layout.demo_layout,container,false); demoButton = (Button) view.findViewById(R.id.demoButton); demoButton.setOnClickListener(this); // Set the listener event for the button return view; } @Override public void onClick(View v) { switch (v.getId()){ case R.id.panhouye: callBackListener.setData("data"); break; } public static interface CallbackListener {public void setData(String data); } } public class MainActivity extends AppCompatActivity implements DemoFragment.CallBackListener{ FragmentManager fragmentManager; FragmentTransaction fragmentTransaction; DemoFragment demoFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize the main layout (the main purpose is to populate fragments for the main layout) initActivity(); } private void initActivity() { fragmentManager = getFragmentManager(); fragmentTransaction = fragmentManager.beginTransaction(); demoFragment = new DemoFragment(); fragmentTransaction.add(R.id.demo,DemoFragment); fragmentTransaction.commit(); } @Override public void setData(String Data) {// Here we receive the value and process it}}

3. The fragments called fragments

Fragments need to pass values from one Fragment to another. To host an Activity, you can combine the above two methods by notifying the Activity with a callback, which then calls the Fragment’s methods.

As a reminder, you can also use JetPack’s LiveData. Multiple Fragments can retrieve the ViewModel of the host Activity, get the same LiveData, and then perform numerical notification. We will discuss more about this in JetPack.

4. How do fragments load lazily?

ViewPager+Fragment is usually used to address this problem. We know that by default ViewPager will preload data from the previous Fragment and the last Fragment. This will have two problems, our front and back pages have not been shown, but the data load request, resulting in a waste of data traffic; If we have reported the data buried point, the data that is not displayed at this time will be reported, which does not meet the needs.

There are two ways to solve this problem, one is to disable ViewPager to prevent preloading, which will not be explained here, and simplicity is not what we are talking about.

The other is the use of lazy loading, in the old version of the package, is through setUserVisibleHint() to achieve lazy loading, more resources on the Internet, interested in you can go to see. In AndroidX, when we use setUserVisibleHint(), we find that the change method has been deprecated. So how to achieve lazy loading? Let’s take a look at the notation for the method, usually deprecation will tell us the alternative method

        /**
         *
         * @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}
         * instead.
         */
        @Deprecated
        public void setUserVisibleHint(boolean isVisibleToUser) {
            ...
        }

As you can see, this tells us that we can use the setMaxLifecycle method of FragmentTransaction instead, so what is setMaxLifecycle? Let’s look at the source code

/** * Set a ceiling for the state of an active fragment in this FragmentManager. If fragment is * already above the received state, it will be forced down to the correct state. * * <p>The fragment provided must currently be added to the FragmentManager  to have it's * Lifecycle state capped, or previously added as part of this transaction. The * {@link Lifecycle.State} passed in must at least be {@link Lifecycle.State#CREATED}, otherwise * an {@link IllegalArgumentException} will be thrown.</p> * * @param fragment the fragment to have it's state capped. * @param state the ceiling state for the fragment. * @return the same FragmentTransaction instance */ @NonNull public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment, @NonNull Lifecycle.State state) { addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state)); return this; }

It is simple to encapsulate the operation as an OP object. Finally, when committing (), the operation is performed according to the OP object. The specific code is relatively simple, but you can check it out yourself

The setMaxLifecycle() comment above basically means that it sets an upper limit for the Fragment lifecycle, and if the limit is exceeded it will be forced down to the appropriate state. So how do we understand that? Let’s start with two things here: Fragment state and LifeCycle state

Fragment state

In the Fragment class, we can see several constants that represent the state of the Fragment

        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 LifeCycle state

Lifecycle is one of the architectural components in JetPack that is used to manage the LifeCycle of activities and fragments. Lifecycle is used to manage the LifeCycle of activities and fragments. Lifecycle is one of the architectural components in JetPack

You can also see in the source code that an enumeration is used in Lifecycle to define the state

public enum State { DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED; public boolean isAtLeast(@NonNull State state) { return compareTo(state) >= 0; }}

At this point we go back to setMaxLifecycle() and the method needs to pass two parameters Fragment and State. Needless to say, the Fragment is the target Fragment to be set, but notice that the Fragment must have been added to the FragmentManager, calling the add() method, otherwise it will throw an exception. State is the enumeration type State in Lifecycle, again it is important to note that the passed State should be at least CREATED, in other words only Created, Started, and Resumed will be passed in otherwise an exception will be thrown as well.

It is important to understand the relationship between Fragment states and the life cycle, as shown in the table

Fragment state process The life cycle
INITIALIZING –> CREATED onAttach, onCreate
CREATED –> INITIALIZING onDetach, onDestory
CREATED –> ACTIVITY_CREATED onCreateView, onActivityCreated
ACTIVITY_CREATED –> CREATED onDestroyView
ACTIVITY_CREATED –> STARTED onStart
STARTED –> ACTIVITY_CREATED onStop
STARTED –> RESUMED onResume
RESUMED –> STARTED onPause

The table above shows the Lifecycle that will be called when each state is switched and now you have to ask the question, how does Fragment and Lifecycle relate to each other? Since State is at least passed in CREATED, we’re only going to have to look at three states. Let’s do the same table

Lifecycle state passed in Fragment state
Lifecycle.State.CREATED Lifecycle.State. Created corresponds to the CREATED State of a Fragment. If the current Fragment State is less than CREATED, i.e. INITIALIZING, then the Fragment State will change to CREATED, Execute the onAttach(), onCreate() methods in turn; If the current Fragment state is higher than Created then the Fragment state is forced to be reduced to Created, taking the current Fragment state for example, then the onPause(), onStop(), and onDestyView () methods are implemented in sequence. If the state of the current Fragment happens to be CREATED, then nothing is done.
Lifecycle.State.STARTED Lifecycle.State. Started corresponds to the Fragment’s Started State. If the current Fragment State is lower than Started, then the Fragment State will change to Started. The onCreateView(), onActivityCreate(), and onStart() methods are executed in turn; If the current Fragment state is higher than Started, then the Fragment state is forced back to Started, and the onPause() method is then performed. If the current Fragment’s state happens to be Started, do nothing.
Lifecycle.State.RESUMED Lifecycle.State. Resumed corresponds to the Fragment State, if the current Fragment State is lower than the Fragment State then the Fragment State will change to the Fragment State, taking the current Fragment State as Started for example, The onResume() method is then executed. If the current Fragment state happens to be Resumed, do nothing.

I don’t know if you understand it or not. If you don’t understand it, please read it several times, or write a demo by yourself to see how the life cycle is printed. You can never go wrong with more hands.

Now back to the question, what should the lazy loading of the ViewPager do?

Android has actually come up with a new way of using SetmaxLifecycle, so it will certainly tell us how to do that, and where is the breakthrough? The FragmentPagerAdapter constructor is out of date, so this is where you start. Let’s look at the source code

/** * Constructor for {@link FragmentPagerAdapter} that sets the fragment manager for the adapter. * This is the equivalent of calling {@link #FragmentPagerAdapter(FragmentManager, int)} and * passing in {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}. * * <p>Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the * current Fragment changes.</p> * * @param fm fragment manager  that will interact with this adapter * @deprecated use {@link #FragmentPagerAdapter(FragmentManager, int)} with * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} */ @Deprecated public FragmentPagerAdapter(@NonNull FragmentManager fm) { this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT); }

To use the new constructor, call Behavior_Resume_Only_Current_Fragment

The BEHAVIOR_SET_USER_VISIBLE_HINT BEHAVIOR_SET_USER_VISIBLE_HINT was assigned to MBehavior by default

    public FragmentPagerAdapter(@NonNull FragmentManager fm,
            @Behavior int behavior) {
        mFragmentManager = fm;
        mBehavior = behavior;
    }

So let’s see where mBehavior is used?

A global lookup finds two places instantiateItem() and setPrimaryItem() methods.

InstantiateItem () is used primarily to instantiate the methods of each Item in the ViewPager.

SetPrimaryItem () is used to set the item to be displayed, and is called whenever viewPager is switched. On the source code

public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) { Fragment fragment = (Fragment)object; if (fragment ! = mCurrentPrimaryItem) { if (mCurrentPrimaryItem ! = null) { mCurrentPrimaryItem.setMenuVisibility(false); if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED); } else { mCurrentPrimaryItem.setUserVisibleHint(false); } } fragment.setMenuVisibility(true); if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED); } else { fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; }}

If the mBehavior value is Behavior_Resume_Only_Current_Fragment, then the setMaxLifecycle() method will be called to set the state of the previous Fragment to Started. Set the state of the Fragment to be displayed to Resumed; If the Behavior_Set_User_Visible_Hint () method of the Fragment is Behavior_Set_User_Visible_Hint (), then the Behavior_Set_UserVisible_Hint () method is used to set the Fragment’s visibility. Accordingly, the getUserVisibleHint() method can be used to get whether the Fragment is visible, so as to achieve lazy loading. I won’t talk about how to do that.

In addition to the table above, we can see that onResume() is executed every time a Fragment goes from invisible to visible

Also, if we were using a FragmentPagerAdapter, the onDestroy () and onDetach() methods would not be executed if we were togglingthe Fragment, only the onDestroyView() method would be executed.

So you know what to do? The code to

Public abstract class BaseLazyFragment extends Fragment {private Boolean isFirstLoad = true; @Override public void onResume() { super.onResume(); if (isFirstLoad) { isFirstLoad = false; lazyInit(); } } @Override public void onDestroyView() { super.onDestroyView(); isFirstLoad = true; Public void lazyInit();} public void lazyInit(); }

conclusion

Here we mainly introduce the function of each life cycle of Fragments, and the problems that Fragments will almost certainly encounter in the process of using them. In fact, there is also data sharing among Fragments based on Jetpack’s Livedata which is not discussed in detail. I plan to discuss it in Jetpack’s article

Fragment is generally used, and we only care about these points: 1. Data transfer; 2. Destruction and reconstruction, 3. Lazy loading. This article is simple to talk about, is generally enough for use, although a lot of details did not say, the follow-up will be made up.

Finally, my technology is not fine, such as mistakes and omissions, welcome to correct the exchange, light spray!