What should Jetpack Lifecycle think? Return liver?

Posted by Allan- on Sun, 06 Mar 2022 05:34:25 +0100

preface

The previous articles are to lay the foundation for Jetpack. This article will officially enter the analysis of various components of Jetpack from Lifecycle.
Through this article, you will learn:

1. Why Lifecycle?
2. How is Lifecycle used?
3. How does Lifecycle perceive the Lifecycle
4. Lifecycle memory leak?
5. Summary

1. Why Lifecycle?

Start of life cycle

Common components with life cycle in Android, such as Activity, Fragment, Service, etc., of which Activity and Fragment are the most common, and the life cycle of Fragment depends on Activity, so it's no big deal to master the life cycle of Activity.
For the story of the ups and downs of the Activity life cycle, please move to:
Detailed explanation and monitoring of Android Activity life cycle

Application of life cycle

Bronze player

Take a simple example: when you enter an Activity (get focus), you need a network request. When the Activity exits (lose focus), you stop the network request. The simplest way is as follows:

    @Override
    protected void onResume() {
        super.onResume();
        NetRequest.startRequest();
    }

    @Override
    protected void onPause() {
        super.onPause();
        NetRequest.stopRequest();
    }

Rewrite the onResume() method. When this method is called, it indicates that the Activity has obtained the focus, and the page will be displayed. At this time, the network request can be made to pull data.
Rewrite the onPause() method. When the method is called, it means that the Activity has lost focus. At this time, there is no need to request the network.
There seems to be no problem, but think about it carefully: for example, when playing a video, you also need to play it in the onResume() method, pause it in the onPause() method, and then, for example, database operation. At this time, many codes are stacked in onResume(), onPause(), making the Activity very bloated.

Silver player

You say it doesn't matter. I have MVP architecture and can use Presenter to encapsulate these business logic.
Declare Presenter class: lifecycle Presenter

public class LifecyclePresenter implements ILifecycle{
    @Override
    public void onResume() {
        NetRequest.startRequest();
    }

    @Override
    public void onPause() {
        NetRequest.stopRequest();
    }
}

This class implements the interface: ILifecycle, and the method declared by this interface is consistent with the Activity life cycle:

interface ILifecycle {
    void onCreate();
    void onStart();
    void onResume();
    void onPause();
    void onStop();
    void onDestroy();
}

OK, finally, monitor the changes in the life cycle in the Activity, and then call different methods of LifecyclePresenter:

    @Override
    protected void onResume() {
        super.onResume();
        lifecyclePresenter.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        lifecyclePresenter.onPause();
    }

In this way, there will be other business logic in the future. You only need to add it in the corresponding method of lifecycle presenter to achieve the effect of reducing the burden on the Activity.

Gold player

Although only one line of code is added to each method in the Activity life cycle, considering the separation of UI and logic, it is best not to couple the two. For example, in the division of labor, some students are responsible for the UI, so they may not care when to start / end the network request. These are written by the students responsible for the specific business logic. How to achieve it?
Remember when analyzing the Activity life cycle:
Activity provides the registeractivitylifecycle callbacks (callback) method, which will be called when the activity lifecycle changes. So we can monitor the life cycle changes here, and then call the corresponding business code:

public class LifecycleHelper {
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static void bindLifecycle(Activity activity, ILifecycle iLifecycle) {
        if (activity == null)
            return;
        activity.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityResumed(@NonNull Activity activity) {
                if (iLifecycle != null)
                    iLifecycle.onResume();
            }

            @Override
            public void onActivityPaused(@NonNull Activity activity) {
                if (iLifecycle != null)
                    iLifecycle.onPause();
            }
        });
    }
}

The Activity passed in is the Activity that wants to listen to the life cycle.

public class LifecyclePresenterV2 {
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public LifecyclePresenterV2(Activity activity) {
        LifecycleHelper.bindLifecycle(activity, new ILifecycle() {
            @Override
            public void onResume() {
                NetRequest.startRequest();
            }

            @Override
            public void onPause() {
                NetRequest.stopRequest();
            }
        });
    }
}

Change the Presenter to: lifecycle presenterv2.
At this point, the Activity only needs to call:

  //this indicates the Activity that needs to be monitored at present
  LifecyclePresenterV2 lifecyclePresenterV2 = new LifecyclePresenterV2(this);

It can be seen that:

There is no need to rewrite the callback methods of each life cycle in the Activity. Just one line of code is needed to put these logic into a separate business layer for processing. The life cycle is bound to the specific business. It depends on how the business needs are linked with the life cycle.
If the Activity is changed later, there is no need to change the business of the logical layer.

Everything seems to be all right? No, look carefully. The registerActivityLifecycleCallbacks() method call has version restrictions: @ RequiresApi(api = Build.VERSION_CODES.Q)
This means that the API must be more than Android 10(29) to call, which has great limitations.

Introduction of Lifecycle

From the above analysis, we can perceive that the interaction scenarios between life cycle and business are very rich, and we really need a set of tools to help us simplify the interaction process.

Google said to have Lifecycle, so it has Lifecycle.

At the outset: the source code of Lifecycle is not complex. It mainly does three things:

1. It provides an interface for the outside world to monitor a certain stage in the life cycle of interest (onResume, onPause, onDestroy, etc.), and the outside world is incarnated as an observer at this time.
2. Listen to the Activity life cycle somewhere. At this time, the Activity is the observed.
3. Inform the observer of the life cycle.

2. How is Lifecycle used?

Add observer

From the three things of Lifecycle, if the outside world wants to monitor the changes of the life cycle, it only needs to add an observation interface and process the corresponding life cycle in the callback method of the interface.
There are two different ways:
###1. Annotate observations
Previous versions of Lifecycle used annotations to simplify the observer in order to minimize rewriting methods in the interface.

    //Implementation of annotation
    class MyObserver7 implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        void onPause() {
            Log.d(TAG, "onPause");
        }
    }

Here we only focus on onPause. If you want to monitor other status changes, you only need to add comments on the corresponding status.
Then listen in the Activity:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle);

        getLifecycle().addObserver(new MyObserver7());
    }

When the Activity loses focus, it will call the onPause() method in MyObserver7.
It can be seen that you only need to add observers in the Activity.
###2. Interface mode processing observation results
The new version of Lifecycle discards the annotation observation mode and uses the interface instead. If you use Java 8 or turn on Java 8 features, it is recommended to use the interface mode.

    //Java 8 mode
    class MyObserver8 implements DefaultLifecycleObserver {
        @Override
        public void onPause(LifecycleOwner owner) {
            Log.d(TAG, "pause");
        }
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lifecycle);
        getLifecycle().addObserver(new MyObserver8());
    }

It can be seen that the DefaultLifecycleObserver interface is implemented, which inherits the FullLifecycleObserver interface.

interface FullLifecycleObserver extends LifecycleObserver {
    void onCreate(LifecycleOwner owner);
    void onStart(LifecycleOwner owner);
    void onResume(LifecycleOwner owner);
    void onPause(LifecycleOwner owner);
    void onStop(LifecycleOwner owner);
    void onDestroy(LifecycleOwner owner);
}

The DefaultLifecycleObserver interface implements all methods of FullLifecycleObserver by default

    @Override
    default void onCreate(@NonNull LifecycleOwner owner) {
    }
    ....

It can be seen that a new feature of Java 8 is used: default modifies the interface method.
Therefore, when we implement the DefaultLifecycleObserver interface, we only need to rewrite the interface corresponding to the concerned state.

3. How does Lifecycle perceive the Lifecycle

To sum up, we can easily perceive the changes of the Activity life cycle through Lifecycle. In the previous part, we also analyze how to encapsulate the life cycle step by step. Unfortunately, we encounter the problem of the version limit of registeractivitylifecycle callbacks. Next, we will explore how Lifecycle can bypass the limit.

Step 1: register observers

LifecycleRegistry

Both the interface and annotation methods use getLifecycle(). This method returns the Lifecycle object, and Lifecycle is an abstract class. getLifecycle() returns the object of Lifecycle subclass: LifecycleRegistry.

#ComponentActivity.java
    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }

When a custom Activity inherits from AppCompatActivity, and ComponentActivity is the parent class of AppCompatActivity, therefore, when a custom Activity calls getLifecycle(), it returns the mlife cycleregistry object.
The mllifecycle owner object in LifecycleRegistry is the custom Activity itself (weak reference).

addObserver()

#LifecycleRegistry.java
    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        Lifecycle.State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        //Pass in observer and construct observer withstate
        LifecycleRegistry.ObserverWithState statefulObserver = new LifecycleRegistry.ObserverWithState(observer, initialState);
        //Add the ObserverWithState to the map
        LifecycleRegistry.ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
        ...
    }

The key is the ObserverWithState:

#LifecycleRegistry.java
    static class ObserverWithState {
        //Lifecycle state
        Lifecycle.State mState;
        //Observer
        LifecycleEventObserver mLifecycleObserver;

        ObserverWithState(LifecycleObserver observer, Lifecycle.State initialState) {
            //Encapsulate observer
            mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
            mState = initialState;
        }

        void dispatchEvent(LifecycleOwner owner, Lifecycle.Event event) {
            //Distribute events and turn event into state
            Lifecycle.State newState = event.getTargetState();
            mState = min(mState, newState);
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }
    }

Look at lifecycle lifecycleEventObserver:

#Lifecycling.java
    static LifecycleEventObserver lifecycleEventObserver(Object object) {
        //Interface mode
        boolean isFullLifecycleObserver = object instanceof FullLifecycleObserver;
        ...
        if (isFullLifecycleObserver) {
            //If the observer is in interface mode, return directly
            return new FullLifecycleObserverAdapter((FullLifecycleObserver) object, null);
        }

        //Process the annotation, record the annotation, and get it directly from the map next time
        final Class<?> klass = object.getClass();
        int type = getObserverConstructorType(klass);
        ...
        return new ReflectiveGenericLifecycleObserver(object);
    }

addObserver() focuses on adding the Observer to the Map after some column encapsulation.

Obviously, we will think that when the life cycle changes, the observer will be taken out of the Map and notified.

Step 2: life cycle change - notify the observer

Android10 (inclusive) and above processing methods

The best way to understand how Lifecycle informs the observer is to view its call stack through a breakpoint.
Taking the interface processing method as an example, when the observer is called, its call stack is as follows:

Notice activity Dispatchactivitypoststarted() method:

#Activity.java
    private void dispatchActivityPostStarted() {
        //Find the listener
        Object[] callbacks = collectActivityLifecycleCallbacks();
        if (callbacks != null) {
            for (int i = 0; i < callbacks.length; i++) {
                //Call callback method
                ((Application.ActivityLifecycleCallbacks) callbacks[i])
                        .onActivityPostStarted(this);
            }
        }
        ...
    }

The listener here is through activity Registeractivitylifecycle callbacks (callback).
The callback is: reportfragment Lifecyclecallbacks object, which is registered in:

#ReportFragment.java
    public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            //If you are above Android 10, you can register activitylifecycle callbacks directly
            ReportFragment.LifecycleCallbacks.registerIn(activity);
        }
        //Add an empty Fragment to the Activity
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.
            manager.executePendingTransactions();
        }
    }

The call time of injectifneedin is in activity Oncreate().
Well, now we know:

When an Activity is created, monitor the change of Activity status through ReportFragment.

According to different system versions, ReportFragment can be monitored in two ways:

1. Devices above Android 10 (inclusive) are monitored through registerActivityLifecycleCallbacks().
2. Below Android 10, it is monitored through Fragment.

So far, Lifecycle only listens to the status of Activity and needs to distribute the status.
Take the processing method of Android 10 as an example:

#ReportFragment.java
    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
        ...
        if (activity instanceof LifecycleOwner) {
            //Get Lifecycle, that is, Lifecycle registry
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                //Distribution event
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }

The final Event is still handled by LifecycleRegistry. In LifecycleRegistry, the corresponding encapsulated observer is found in the Map according to the transformation of Event and State, and the observer registered in Activity is finally notified. So far, a notification of life cycle State change is completed.

Android 10 the following processing methods

We know that when a Fragment is associated with an Activity, its life cycle is linked with the Activity. Please move to: What does Android Fragment want you to do?

Therefore, when the life cycle of an Activity changes, a Fragment method will be called. Take onStart() status as an example:

#ReportFragment.java
    @Override
    public void onStart() {
        super.onStart();
        dispatchStart(mProcessListener);
        //Distribution event
        dispatch(Lifecycle.Event.ON_START);
    }

    private void dispatch(@NonNull Lifecycle.Event event) {
        if (Build.VERSION.SDK_INT < 29) {
            //Only when Android is below 10
            dispatch(getActivity(), event);
        }
    }

At this point, we know how Lifecycle handles the version limitation of registerActivityLifecycleCallbacks(): that is, it shields the upper callers from the details of monitoring the changes of Activity life cycle through ReportFragment. Use registerActivityLifecycleCallbacks() on Android 10 and above, and add empty fragments under it to the Activity.

Lifecycle aware lifecycle summary

It is shown as follows:

Green part:

Lifecycle monitors Activity lifecycle changes.

Blue part:

The outside world registers observers through Lifecycle.

Red part:

Lifecycle monitors changes in the lifecycle and notifies the observer.

4. Lifecycle memory leak?

Add the following code to the Activity (assuming it is customized as LifeActivity) onCreate() to monitor the Activity life cycle:

        getLifecycle().addObserver(new DefaultLifecycleObserver() {
            @Override
            public void onCreate(@NonNull @org.jetbrains.annotations.NotNull LifecycleOwner owner) {
                
            }
        });

Here's the question: do you need to be in activity Remove the observer from ondestroy()?
We know that the anonymous inner class holds the external class reference, addObserver(xx). This xx object holds the Activity. Generally speaking, when we see this writing, we are immediately associated with it: if xx is not released, when the Activity is destroyed, the Activity object cannot be released because it is held by xx.
So most of the time we need to be in activity removeObserver(xx) in ondestroy().

Next, let's take a look at getlifecycle() Addobserver (xx) whether the observer needs to be removed actively in this case. The key question is who owns xx.
According to the previous analysis, xx is encapsulated as an ObserverWithState object and finally stored in the Map. The Map is:

#LifecycleRegistry.java
    private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
            new FastSafeIterableMap<>();

It is the member variable of LifecycleRegistry, and the LifecycleRegistry object is the member variable of ComponentActivity. The final result: Map is the member variable of LifeActivity.
When the Activity is destroyed, the Map will be destroyed and the Map will no longer hold the Observer.
Therefore, it is concluded that:

There is no need to call getlifecycle() removeObserver(observer);

The essence of memory leakage is that long-lived objects hold references to short-lived objects, which makes short-lived objects unable to be released.

5. Summary

This article does not focus on Lifecycle And Lifecycle.State The transformation between events focuses on why Lifecycle is needed and how Lifecycle perceives the Lifecycle, hoping to naturally introduce Lifecycle and understand its design principle.
Lifecycle is the foundation of LiveData. Next, we will analyze the wonderful use of LiveData.

This article is based on: implementation 'Android X appcompat:appcompat:1.4.1’

Lifecycle source code test

If you like it, please praise and pay attention. Your encouragement is my driving force

Continuously updating, work with me step by step to learn more about Android

Topics: Python Android Mini Program