Android Fragment lazy loading new ideas

Posted by dreams4000 on Wed, 09 Feb 2022 13:37:18 +0100

Before Android x, we implemented lazy loading, usually through setUserVisibleHint method to control whether the Fragment is visible. After Android x, Google provided us with a new solution. Today we will learn.

If you directly use the previous scheme in Android x, you will be prompted that the method is outdated:

Click to view the notes:

Maybe this method can tell whether the current Fragment is visible to the user, but it can be called outside the life cycle, so the method callback in the life cycle may not be very accurate. So in Android x, Google recommends that we use setMaxLifecycle

Let's click in to see this method:

/**
 * 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;
}

Through this method, we can set the maximum life cycle limit of the Fragment, that is, you can set the maximum life cycle limit of the Fragment. If the execution of the life cycle exceeds the set Fragment, it will be forcibly reduced to the correct life cycle. Moreover, the minimum limit of the incoming life cycle must be Create, that is, onCreateView is executed.

The following statuses are provided:

Let's use a Demo to demonstrate:

xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.cloud.fragment.TestActivity">

</FrameLayout>

Activity:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        StatusBarUtil.setTranSparentBar(this);
        setContentView(R.layout.activity_test);
        Fragment1 fragment1 = Fragment1.newInstance();
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragment, fragment1)
                .setMaxLifecycle(fragment1, Lifecycle.State.CREATED)
                .commit();
        
    }

Fragment1

public class Fragment1 extends BaseFragment {
 		....

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("demo", "f1-onStart");
    }
}

The above codes are very simple, and unnecessary parts are omitted, mainly the use of setMaxLifecycle. Please pay attention to:

Run our simulator:

Because onResume is not executed, our interface cannot be seen

The log is as follows:

Change to Resume

   getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.fragment, fragment1)
                .setMaxLifecycle(fragment1, Lifecycle.State.RESUMED)
                .commit();

The log is as follows:

The effect is as above, and there is nothing to say about the method used.

The rest are almost the same.

Now let's take a look at the new scheme of lazy loading:

Let's take a look at the FragmentPagerAdapter class first:

A new parameter is added to control which mode is selected. Now two modes are provided, as follows:

Among them, behavior is used by default_ SET_ USER_ VISIBLE_ Hint, as for what they do, let's continue to look at them in detail: of course, combined with our initial Demo, we can also see the clue from the notes.

Because Android x abolished the setUserVisibleHint() method, the corresponding adapter has also been changed:

Here you can see that the two parameters added above are used here.

Let's take a look at the logic inside. By default, lifecycle is used State. Resumed, that is, the lifecycle method will be executed to onResume by default, and it replaces the setUserVisibleHint(true) method, which is used to inform whether the current fragment is visible to the user. Then use BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT replaces setUserVisibleHint(false).

Thus, the default FragmentPagerAdapter construction method will make the fragment execute to onResume, and the construction parameter behavior is specified_ RESUME_ ONLY_ CURRENT_ Fragment, it will only be executed to onStart.

With this feature, let's try a new lazy loading scheme:

The demo is also very simple. You only need to pass in behavior when you need a new FragmentAdapter_ RESUME_ ONLY_ CURRENT_ Fragment.

new FragmentAdapter(getSupportFragmentManager(),FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,list)

There is no need to demonstrate the specific code.

Let's look at the log changes through log.

Test demo: ViewPager-Fragment1-Fragment2

When opening an Activity:

When switching to the second Fragment:

It can be observed that onResume is executed every time, so we can put our data loading method in onResume. The simple Demo is as follows:

public abstract class BaseFragment extends Fragment {

    private View view;

    public abstract int setView();

    public abstract void initView();

    public abstract void initData();

    private volatile boolean onVisible = false;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(setView(), null);
        onVisible = true;
        initView();
        return view;
    }


    @Override
    public void onResume() {
        super.onResume();
      	//
        if (onVisible) {
            initData();
            onVisible = false;
        }
        updateData();
    }
  
   @Override
    public void onPause() {
        super.onPause();
      	//	
    }

    private void updateData() {

    }
    

    public View getView() {
        return view;
    }
}

Therefore, our specific operations can be put into onResume and onPause, corresponding to visible or invisible respectively, which is very simple.

Well, it's a great honor if I can help you. If you have any questions or questions, you are also welcome to leave a message to learn.