ViewLifecycleOwner is recommended

Android Studio prompts you to use viewLifecycleOwner when you call the Observe method on the LiveData object in your Fragment and pass the LifecycleOwner argument as this. As shown below:

From the type, type of fragments and viewLifecycleOwner FragmentViewLifecycleOwner both inherited LifecycleOwner, to directly use this before, in most cases is completely normal running. So what is the Lint alert for here?

By looking on the website for androidx. Fragments, fragments of a document, you can see 1.2.0 version has the following an update:

It can be seen that this is intentional, and the reasons are analyzed below.

ViewLifecycleOwner The related life cycle

Trace the relevant code for viewLifecycleOwner (irrelevant code has been omitted) :

//Fragment.java    
    void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        mViewLifecycleOwner = newFragmentViewLifecycleOwner(); mView = onCreateView(inflater, container, savedInstanceState); . }Copy the code
//FragmentManager.java 	    
    private void destroyFragmentView(@NonNull Fragment fragment) {
        // The downstream code calls onDestroyView()
        fragment.performDestroyView();
        fragment.mViewLifecycleOwner = null; . }Copy the code

As you can see, mViewLifecycleOwner is assigned before onCreateView and null after onDestroyView. The mViewLifecycleOwner comment also indicates this:

This is initialized in performCreateView and unavailable outside of the onCreateView/onDestroyView lifecycle.

As shown in the name of the class, the FragmentViewLifecycleOwner represent fragments in the View of LifecycleOwner, fragments in the View of life cycle with fragments itself is not the same. Review the Fragment life cycle:

The Fragment entering the back stack will execute onDestroyView without onDestroy and onDetach, and the stack leaving the back stack is executed from onCreateView without onAttach and onCreate.

The Observe process of LiveData

Now that the relevant lifecycle is sorted out, let’s take a look at LiveData’s Observe process.

We know that in LiveData’s observe method, the owner and observer parameters will be wrapped, so that the observer callback will be affected by the owner’s life cycle. Observer will be called only when the owner is active. Active status means that the current owner’s life cycle is at least STARTED, which is the method of determining whether the owner and observer wrapped classes are active or not in (1) below.

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }
        @Override
        boolean shouldBeActive(a) {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }/ / 1
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);/ / 2
                return; } activeStateChanged(shouldBeActive()); }... }Copy the code

However, the active lifecycle state of a Fragment is the same as that of a View in the Fragment. There is no difference between the owner using the Fragment or the viewLifecycleOwner. The only difference is onCreateView to onDestroyView.

The onStateChanged method in the wrapper class performs an in-time removal of the Observer, which happens at (2) while the owner is in DESTROYED. For fragments, the state changes to DESTROYED before onDestroyView is executed, as shown below:

//Fragment.java	
    void performDestroyView(a) {
        mChildFragmentManager.dispatchDestroyView();
        if(mView ! =null) { mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); } mState = CREATED; onDestroyView(); . }Copy the code

why

At this point, it makes sense to use viewLifecycleOwner instead of this when calling the Observe method on the LiveData object in your Fragment: The life cycle of a View in a Fragment is not the same as that of the View in the Fragment. You need to let the observer perceive the life cycle of the View in the Fragment instead of the View in the Fragment. Therefore, Android creates a LifecycleOwner for the View in the Fragment. ViewLifecycleOwner namely.

We can easily construct a scenario using the Fragment’s back stack to show the difference between the two.

val handler = Handler(Looper.getMainLooper())

class BlankFragment1 : Fragment() {
    val data = MutableLiveData<Int> ()override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.fragment_blank1, container, false)}override fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)
        tv.setOnClickListener {
            parentFragmentManager.beginTransaction()
                .replace(R.id.container, BlankFragment2())
                .addToBackStack(null)
                .commit()/ / 1
            handler.postDelayed({
                data.value = (data.value ? :0) + 1
            }, 2000)/ / 2
        }
        data.observe(viewLifecycleOwner, Observer {
            Log.e("viewLifecycleOwner"."count: ${data.value}")})/ / 3
        data.observe(this, Observer {
            Log.e("fragment"."count: ${data.value}")})/ / 4}}Copy the code

Add the above BlankFragment1 to the Activity’s FrameLayout container, and when you click on TV, replace BlankFragment1 with BlankFragment2. Note that BlankFragment1 is added to the back Stack. Then delay 2s and modify the LiveData value at (2). In addition, the LiveData declaration is more appropriate in the Viewmodel, so it is easier to write in the Fragment. Observe data in (3) and (4) respectively.

After switching to BlankFragment2, wait for 2s, return to BlankFragment1, and view the print as follows:

2021-01-08 10:35:19.482 27156-27156/com.caz. Test E/fragment: count: 1 2021-01-08 10:35:19.482 27156-27156/com.caz. Test E/fragment: count: 1 2021-01-08 10:35:19.482 27156-27156/com.caz.test E/viewLifecycleOwner: count: 1Copy the code

You can see that the LifecycleOwner log is printed twice with this because when BlankFragment1 returns, the onCreateView to onDestroyView lifecycle method is re-executed. (4) The Observer with the same logic bound to the Fragment View life cycle was added twice, and (3) the Observer with the Fragment View life cycle was removed when the View was replaced. Only the newly added Observer is left when BlankFragment1 returns.

Android recommends using viewLifecycleOwner precisely because, in almost all cases, our Observer logic is related to the View lifecycle in the Fragment.