Lifecycle is a cornerstone of any GUI programming and Android is no exception.

As a user, we expect a smooth and consistent experience in daily operations such as page hopping, screen rotation, checking notifications, and switching apps. Behind this lies the logic that we developers need to perform at different stages of the lifecycle component. The lifecycle component here could be an entire application or a single page. In Android, activities, fragments, and even services can be lifecycle components.

Now imagine a requirement that you encapsulate the basic components of a player for various departments to call. The basic process is to start the Activity, initialize the resource and play, exit the Activity, and stop playing.

It’s easy to write code like this without thinking about it.

class ShitAVPlayer {
    fun init() {}
    fun start() {}
    fun stop() {}
}
Copy the code

The caller needs to call the corresponding method in the different lifecycle callbacks of the Activity/Fragment lifecycle component.

class VideoActivity : AppCompatActivity() {
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        shitAVPlayer.init()
        shitAVPlayer.start()
    }
​
    override fun onStop() {
        super.onStop()
        shitAVPlayer.stop()
    }
}
Copy the code

There are no major problems with the functionality, but the following scenarios will inevitably give people a feeling of turning over shit in a mountain of shit.

  1. Your ShitAVPlayer is provided as a base component for other departments to use, and when you reach a point where there is no way to guarantee that every caller will properly handle the lifecycle, you will be running around with memory leaks.

– what? You blamed me for not calling it yourself?

— Who knows how much logic I have to process in a lifecycle callback? Your ShitAVPlayer is exactly what it sounds like!

  1. If your ShitAVPlayer needs to be initialized in onCreate(), it provides an asynchronous callback to indicate the initialization status to determine whether it can be enabled. You may not be able to guarantee that the lifecycle component is already onStop() when the callback is executed, and that’s up to the caller to handle. This can lead to undesirable life cycle situations.

    override fun onCreate(savedInstanceState: Bundle?) {super.oncreate (savedInstanceState) shitavPlayer. init {success -> // If (success) shitavPlayer.start ()}}Copy the code
  2. If the product requires you to press the Home button to return to your desktop to pause the playback and resume it when you return. You add onResume() and onPause() to ShitAVPlayer and then spend a lot of time synchronizing with the various callers.

    — Add a method to your Activity’s onResume and onPause callbacks, 😜

    — Plus you xx!

In line with the human fallibility principle, the basic discipline of a good foundation component is the ability to do things yourself without bothering others.

In order for ShitAVPlayer to become an ExcellentAVPlayer, you need to give the component the ability to sense the external lifecycle.

Lifecycle based on the observer pattern

To comb through the requirements, we need to make ShitAVPlayer automatically aware of life cycle events or callbacks of external life cycle components. There are three roles:

  1. A lifecycle component might as well be called LifecycleOwner. The Activity/fragments/Service and so on any component has a life cycle, can be regarded as LifecycleOwner.
  2. A LifecycleObserver might as well be called a LifecycleObserver. Objects such as ShitAVPlayer need to be life-cycle aware.
  3. Lifecycle ontology. In the object-oriented world, there will inevitably be a need for a Lifecycle class to represent the Lifecycle.

Obviously, this is a typical observer pattern, and the LifecycleObserver watches LifecycleOwner life cycle changes.

Add methods like addObserver, removeObserver to LifecycleOwner to follow the basic pattern of observer. However, considering that Lifecycle components in Android can have activities, fragments, services, etc., it is not appropriate for Lifecycle to implement the code logic directly. At the same time let LifecycleOwner hold Lifecycle, as much as possible on/off.

public interface LifecycleOwner {
    Lifecycle getLifecycle();
}
Copy the code

The core logic of the Observer pattern is handed over to Lifecycle and will provide three main functions:

  1. Add/remove observer
  2. Receives life cycle events passed by external life cycle components
  3. Synchronizes the life cycle state of the internal observer
Public class Lifecycle {// Save the observer and its Lifecycle State private Map<LifecycleObserver,State> mObserverMap =.... Public void addObserver(LifecycleObserver observer) {} // removeObserver public void removeObserver(LifecycleObserver) LifecycleOwner public void handleLifecycleEvent(Lifecycle.Event Event) { Derive the life cycle state that should be in moveToState(Event.gettarGetState ()); } private void moveToState(State next) {// Sync (); } private void sync() {observermap.foreach {observer -> // call the corresponding lifecycle callback observer.onxxx ()}}Copy the code

For the Observer LifecycleObserver, there is a default interface that provides the ability to handle the lifecycle.

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);
}
Copy the code

To avoid redundant code, you can take advantage of Java 8’s default interface implementation for simplicity.

public interface DefaultLifecycleObserver extends FullLifecycleObserver { @Override default void onCreate(LifecycleOwner  owner) {} @Override default void onStart(LifecycleOwner owner) {} @Override default void onResume(LifecycleOwner owner)  {} @Override default void onPause(LifecycleOwner owner) {} @Override default void onStop(LifecycleOwner owner) {} @Override default void onDestroy(LifecycleOwner owner) {} }Copy the code

Now it’s easy to optimize the ShitAVPlayer and let it implement DefaultLifecycleObserver directly.

class ExcellentAVPlayer : DefaultLifecycleObserver {
​
    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        init { success -> 
          if (success && lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            start()
          }
        }
    }
​
    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        resume()
    }
​
    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
        pause()
    }
​
    override fun onStop(owner: LifecycleOwner) {
        super.onStop(owner)
        stop()
    }
    
    private fun start() {}
​
    private fun stop() {}
​
    private fun pause() {}
​
    private fun resume() {}
​
    private fun init(action:(Boolean)->Unit){
​
    }
}
Copy the code

For the caller, it only takes one line of code to solve the lifecycle problem once and for all.

class VideoActivity : AppCompatActivity() {
​
    private val excellentAVPlayer by lazy { ExcellentAVPlayer() }
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // here
        lifecycle.addObserver(excellentAVPlayer)
    }
}
Copy the code

Necessity of Event and State

Lifecycle events and lifecycle states, I don’t know if you noticed those two terms in the last section.

Why introduce Events and States in a life cycle notification mechanism based on the observer pattern? Is it ok just to be based on events?

LifecycleOwner distributes events in its own Lifecycle callbacks, Lifecycle receives the Event and calculates the corresponding State. All LifecycleObservers in the observer queue are then synchronized to this State. Finally, the corresponding method for LifecycleOvserver is called back based on the Event. So what does State do?

An Event is a point for notification of life cycle events. State, on the other hand, is a block that represents a phase of the lifecycle and helps third-party components that need to rely on the lifecycle component to quickly get the current lifecycle phase and do something reasonable. Like the init callback in ExcellentAVPlayer,

Init {success - > / / only STARTED phase need to start () if (success && lifecycle. The currentState. IsAtLeast (lifecycle. State. STARTED)) { start() } }Copy the code

Init () is a time-consuming process. When the initialization is complete, you need to determine whether you need to start(). At this point, you need to get the current generation of the lifecycle component to give you the cycle State.

The corresponding relationship between Event and State is also easy to understand. I drew a picture.

State.isatleast (Lifecycle.state. STARTED) is the phase from ON_START to ON_STOP.

By cooperating with Event and State, the third-party components can not only perceive life cycle changes, but also obtain the current life cycle State anytime and anywhere, so as to solve various life cycle problems easily.

Lifecycle will solve the problem

The above pseudo-code and the specific source code implementation has a big difference, this article aims to grasp the overall idea, not too much attention to the source details. To summarize, what problem does Lifecycle of the observer pattern solve?

Give components the ability to sense the external life cycle, decouple tedious boilerplate code to the components, and save bloated life cycle code. Prevent unexpected life cycle situations from occurring.

Lifecycle is the core foundation of Jepack and even the entire operating system, and it is quite necessary to read the Fucking Souce Code. See you next!