Use lifecycle aware components to handle the lifecycle

Lifecycle aware components perform operations in response to changes in the lifecycle state of another component, such as activities and fragments. These components help you generate more organized and often lightweight code that is easier to maintain.

A common pattern is to implement component-dependent operations in the lifecycle methods of activities and fragments. However, this pattern leads to poorly organized code and bug proliferation. By using lifecycle aware components, you can move component-dependent code out of lifecycle methods and into the components themselves.

The Android.Arch. lifecycle package provides classes and interfaces that allow you to build lifecycle aware components that automatically adjust their behavior based on the current lifecycle state of an activity or fragment.

Most application components defined in the Android framework have a lifecycle attached. The lifecycle is managed by the framework code running in the operating system or process. They are at the heart of how Android works, and your applications must follow them. Failure to do so may trigger a memory leak or even an application crash.

Imagine that we have an activity that shows device location on the screen. A common implementation might look something like this:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}
Copy the code

Even if this example looks good, in a real application you end up with too many calls to manage the UI and other components in response to the current state of the life cycle. Managing multiple components puts a lot of code in lifecycle methods, such as onStart() and onStop(), which makes them difficult to maintain.

In addition, there is no guarantee that the component will start before the activity or fragment stops. Especially if we need to run an operation for a long time, such as checking some configuration in onStart(). This can lead to a race hazard that causes the onStop() method to be called before onStart() completes, keeping the component active longer than it needs to.

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if(result) { myLocationListener.start(); }}); } @Override public voidonStop() { super.onStop(); myLocationListener.stop(); }}Copy the code

The Android.Arch. lifecycle package provides classes and interfaces to help you solve these problems in an elastic and isolated manner.

1.Lifecycle

Lifecycle is a class that contains information about the Lifecycle state of a component (such as an activity or fragment) and allows other objects to observe that state.

Lifecycle uses two main enumerations to track the Lifecycle status of its associated components:

Event

Lifecycle events scheduled from the framework and Lifecycle classes. These events map to callback events in activities and fragments.

State

Lifecycle object tracks the current state of the components.

Think of states as nodes of the graph and events as lines between these nodes.

Classes can monitor the lifecycle status of components by adding annotations to their methods. You can then add an observer by calling the addObserver() method of the Lifecycle class and passing an instance of the observer, as shown in the following example:

public class MyObserver implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void connectListener() {... } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public voiddisconnectListener() {... } } myLifecycleOwner.getLifecycle().addObserver(new MyObserver());Copy the code

In the example above, the myLifecycleOwner object implements the LifecycleOwner interface, as explained in the next section.

2.LifecycleOwner

LifecycleOwner is a single method interface that indicates that the class has a Lifecycle. It has a method getLifecycle() that the class must implement. See ProcessLifecycleOwner if you are trying to manage the life cycle of an entire application process.

This interface abstracts the ownership of Lifecycle from individual classes such as fragments and appcompatactivities and allows writing components to run with him. Any custom application class can implement the LifecycleOwner interface.

Components that implement LifecycleObserver can be seamlessly combined with components that implement LifecycleOwner because the owner can provide the lifecycle and observers can register for observations.

For the location tracking example, we can have the MyLocationListener class implement LifecycleObserver and then use the activity’s Lifecycle to initialize it in the onCreate() method. This allows the MyLocationListener class to be self-sufficient, which means that the logic that responds to life cycle state changes is replaced with MyLocationListener declared in the activity. This allows each component to store its own logic, making the logic for activities and fragments easier to manage.

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if(result) { myLocationListener.enable(); }}); }}Copy the code

A common example is to avoid calling certain callbacks if Lifecycle is not now in the correct Lifecycle state. For example, if this callback runs a fragment transaction after the activity’s state is saved, this will trigger a crash, so we never want to invoke the callback.

To simplify this use case, the Lifecycle class allows other objects to query the current state.

class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }
}
Copy the code

With this implementation, our LocationListener class is fully lifecycle aware. If we need to use the LocationListener in another activity or fragment, we simply initialize it. All setup and removal operations are managed by the class itself.

If a library provides classes that need to use the Android lifecycle, we recommend that you use lifecycle aware components. Your library client can easily integrate these components without requiring manual lifecycle management on the client side.

Implement a custom LifecycleOwner

Fragments and Activities implement the LifecycleOwner interface in the Support Library 26.1.0 and later.

If you have a custom class that wants to create a LifecycleOwner, you can use the LifecycleRegistry class, but need to forward events to the class, as shown in the following code example:

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        returnlifecycleRegistry; }}Copy the code

3. Best practices for lifecycle aware components

  • Keep UI controllers (Activities and fragments) as lean as possible. They shouldn’t try to get their own data; Instead, use the ViewModel to process the data and observe the LiveData object to reflect changes back to the view.

  • Try writing a data-driven UI where your UI controller is responsible for updating the view when data changes or notifying the ViewModel of user actions.

  • Put your data logic in your ViewModel class. The ViewModel should act as a connector between the UI controller and the rest of the application. Be careful, however, that the ViewModel is not responsible for retrieving data (for example, from the network). Instead, the ViewModel should call the appropriate component to fetch the data and feed the results back to the UI controller.

  • Use Data Binding to maintain view and UI controls to keep the interface cleaner. This makes the view more declarative and minimizes the need to write updated code in Activities and fragments. If you prefer to do this in the Java programming language, use a library like Butter Knife to avoid boilerplate code and have better abstractions. (This avoids a lot of FindViewById in the program)

  • If your UI is complex, consider creating a Presenter class to handle UI changes. This can be a daunting task, but it can make your UI components easier to test.

  • Avoid referencing a View or Activity context in your ViewModel. If the ViewModel exceeds the Activity (in the case of a configuration change), your Activity will leak and the garbage collector will not handle it properly.

  • Use Kotlin coroutines to manage long-running tasks and other operations that can run asynchronously.

ViewModel, LiveData, etc., are introduced in the above best practices, which will be covered in a later chapter, but also show that the whole AAC section is closely linked.

4. Use cases for lifecycle aware components

Lifecycle awareness components make it easier for you to manage the lifecycle in a variety of situations. Some examples are:

  • Toggle between coarse-grained and fine-grained position updates. Use lifecycle awareness components to enable fine-grained location updates when your location application is visible and switch to coarse-grained updates when the application is in the background. LiveData, a lifecycle aware component, allows your application to automatically update the UI when the user changes location.

  • Stop and start video buffering. Use lifecycle aware components to start video buffering as soon as possible, but delay playback until the application is fully started. You can also use lifecycle aware components to terminate buffering when the application is destroyed.

  • Start and stop network connections. Use lifecycle awareness components to enable real-time updates (streaming) of network data while the application is in the foreground and to automatically pause when the application is in the background.

  • Pause and resume animation drawables. Use a lifecycle aware component to handle suspending animated drawables while the app is in the background and resuming drawables after the app is in the foreground.

5. To summarize

Life cycle is very important for our application, for the life cycle of treatment, through the life cycle of perception in AAC component can be very good deal with application lifecycle, bring a better user experience, this article as an example, to monitor the user’s address changes in good embodies the life cycle of the importance of components for the application. The combination of ViewModel, LiveData and Data Binding components can make our APP more robust architecture.

  • Android Jetpack Architecture Component family first introduction
  • Android Jetpack Architecture component series 2 DataBinding
  • Android Jetpack architecture component series three life cycle aware components
  • Android Jetpack architecture component series four LiveData