This is the 16th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

Many people use the combination of ViewPager + Fragment, and there is a need for lazy loading of the Fragment. How to implement lazy loading of the Fragment

Fragment lazy loading method

With the help of setUserVisiblity, the specific implementation is as follows:

@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; onVisible(); } else { isVisible = false; onInVisible(); }}Copy the code

The setUserVisiblity method has a Deprecated annotation (@deprecated) that will be Deprecated in the future.


/**
 * @return The current value of the user-visible hint on this fragment.
 * @see #setUserVisibleHint(boolean)
 *
 * @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}
 * instead.
 */
@Deprecated
public boolean getUserVisibleHint() {
    return mUserVisibleHint;
}
Copy the code

Yes, you were right about setMaxLifecycle.

AndroidX has added a new method for FragmentTransaction called setMaxLifeCycle starting from 1.0-alpha07. We recommend that developers replace setUserVisibleHint with this method because it will provide the following benefits:

  1. Lifecycle based lazy loading is more scientific and can be used in MVVM architectures with components such as Livedata
  2. SetMaxLifeCycle does not require an additional Fragment base class to be used without incursion

So how do you use setMaxLifecycle to implement lazy loading of fragments

How setMaxLifecycle is used

FragmentPagerAdapter constructor of a new behaviors parameters, when the parameter is set to FragmentPagerAdapter. BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, SetMaxLifecycle limits the life of the Fragment by executing onResume() only when the Fragment is displayed on the screen. This allows lazy loading of data by placing the method in onResume() :

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle? , persistentState: PersistableBundle?) { super.onCreate(savedInstanceState, persistentState) setContentView(R.layout.activity_main) val viewPager: ViewPager = findViewById(R.id.viewpager) val fragmentList: MutableList<Fragment> = ArrayList() fragmentList.add(Fragment1()) fragmentList.add(Fragment2()) FragmentList. Add (Fragment3 ()) / / for MyPagerAdapter adapter installed FragmentPagerAdapter BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT parameter val myPagerAdapter: MyPagerAdapter = MyPagerAdapter( getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, FragmentList) viewPager.setAdapter(myPagerAdapter) // Set preload to 3 pages, To test the lazy loading the success of a viewPager. OffscreenPageLimit = 3} class MyPagerAdapter (FM: FragmentManager, behaviors: Int, val fragmentList: List<Fragment> ) : FragmentPagerAdapter(fm, behavior) { override fun getCount() = fragmentList.size override fun getItem(position: Int) = fragmentList[position] } }Copy the code

The FragmentPagerAdapter calls setMaxLifecycle based on the behavior after the Fragment is created.

//FragmentPagerAdapter.java public FragmentPagerAdapter(@NonNull FragmentManager fm, @Behavior int behavior) { mFragmentManager = fm; mBehavior = behavior; } @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { ... if (fragment ! = mCurrentPrimaryItem) { fragment.setMenuVisibility(false); If (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {// Initialize the item with its lifecycle limited to STARTED mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED); } else {/ / compatible with the old logic fragments. SetUserVisibleHint (false); } } return fragment; } @Override 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) { ... / / slip away will become the main item, set its Lifecycle is STARTED mCurTransaction. SetMaxLifecycle (mCurrentPrimaryItem, Lifecycle. State. STARTED); } else { mCurrentPrimaryItem.setUserVisibleHint(false); } } fragment.setMenuVisibility(true); if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { ... / / set up the new slide to the main item of the Lifecycle of RESUMED mCurTransaction. SetMaxLifecycle (fragments, Lifecycle. State. RESUMED); } else { fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; }}Copy the code

As you can see from the source code, even without behavior, it is equivalent to call setMaxLifecycle directly when you build Framgent in a custom Adapter.

SetMaxLifecycle implements lazy loading

All operations on fragments will be converted to an Op by FramgentTransition. There is also a new Op for setMaxLifecycle, OP_SET_MAX_LIFECYCLE, which deals with life cycle constraints. When FramgentTransition adds OP_SET_MAX_LIFECYCLE to an ament, In its implementation class, BackStackRecord, FragmentManager iterates through the Op list of transactions and sets the Fragment’s mMaxState to indicate the maximum lifetime it is allowed to live. The setting of mMaxState is done through the setMaxLifeCycle method of the same name in FragmentManager. ### FragmentStateManager

Once setMaxLifecycle is called, the FragmentManager puts limits on the Fragment life cycle through the FragmentStateManager.

It’s worth noting that FragmentStateManager is a new class that was added after 1.3.0-Alpha08. It pulls state-related logic away from FragmentManager, reducing much of the Fragment coupling and making it more monolithic.

Conclusion:

In addition to using the default BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, We can even set the Fragment’s MaxLifecycle to CREATED in our custom Adapter instantiateItem so that the Fragment will only go onCreate and delay more operations. For example, some operations on the inflate in onCreateView and onViewCreated.