An Android Jetpack Lifecycle Tutorial

Revision as of 13:33, 12 June 2018 by Neil (Talk | contribs)

Revision as of 13:33, 12 June 2018 by Neil (Talk | contribs)

The previous chapter provided an overview of lifecycle awareness and outlined the key classes and interfaces that make this possible with an Android app project. This chapter will build on this knowledge base by building an Android Studio project designed to highlight lifecycle awareness in action.

Creating the Example Lifecycle Project

Begin this example by launching Android Studio and creating a new project, entering LifecycleDemo into the Application name field and ebookfrenzy.com as the Company Domain setting before clicking on the Next button.

On the form factors screen, enable the Phone and Tablet option and set the minimum SDK setting to API 27). Continue through the setup screens, requesting the Activity & Fragment+ViewModel option using the default activity, fragment and view model names.

Creating a Lifecycle Observer

As previously discussed, activities and fragments already implement the LifecycleOwner interface and are ready to be observed by other objects. To see this is practice, the next step in this tutorial is to add a new class to the project that will be able to observe the MainFragment instance.

To add the new class, right-click on app -> java -> com.ebookfrenzy.lifecycledemo in the Project tool window and select New -> Java Class... from the resulting menu. In the Create New Class dialog, name the class DemoObserver and click on the OK button to create the DemoObserver.java file. The new file should automatically open in the editor where it will read as follows:

package com.ebookfrenzy.lifecycledemo;

public class DemoObserver {
}

Remaining in the editor, modify the class file to declare that it will be implementing the LifecycleObserver interface:

package com.ebookfrenzy.lifecycledemo;

import androidx.lifecycle.LifecycleObserver;

public class DemoObserver implements LifecycleObserver {
    
}

The next step is to add the lifecycle methods and assign them as the lifecycle event handlers. For the purposes of this example, all of the events will be handled, each outputting a message to the Logcat panel displaying the event type. Update the observer method as outlined in the following listing:

package com.ebookfrenzy.lifecycledemo;

import android.util.Log;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;

public class DemoObserver implements LifecycleObserver {

    private String LOG_TAG = "DemoObserver";

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
        Log.i(LOG_TAG, "onResume");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
        Log.i(LOG_TAG, "onPause");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {
        Log.i(LOG_TAG, "onCreate");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart() {
        Log.i(LOG_TAG, "onStart");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {
        Log.i(LOG_TAG, "onStop");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        Log.i(LOG_TAG, "onDestroy");
    }
}

So that we can track the events in relation to the current state of the fragment, an ON_ANY event handler will also be added. Since this method is passed a reference to the lifecycle owner, code can be added to obtain the current state. Remaining in the DemoObserver.java file, add the following method:

@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
    Log.i(LOG_TAG, owner.getLifecycle().getCurrentState().name());
}

With the DemoObserver class completed the next step is to add it as an observer on the MainFragment class.


Adding the Observer

Observers are added to lifecycle owners via calls to the addObserver() method of owner’s Lifecycle object, a reference to which is obtained via a call to the getLifecycle() method. Edit the MainFragment.java class file and add code to the onActivityCreated() method to add the observer:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);

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

With the observer class created and added to the lifecycle owner’s Lifecycle object, the app is ready to be tested.

Testing the Observer

Since the DemoObserver class outputs diagnostic information to the Logcat console, it will be easier to see the output if a filter is configured to display only the DemoObserver messages. Using the steps outlined previously in TBD, configure a filter for messages associated with the DemoObserver tag before running the app on a device or emulator.

On successful launch of the app, the Logcat output should indicate the following lifecycle state changes and events:

onCreate
CREATED
onStart
STARTED
onResume
RESUMED

With the app still running, perform a device rotation to trigger the destruction and recreation of the fragment, generating the following additional output:

STARTED
onStop
CREATED
onDestroy
DESTROYED
onCreate
CREATED
onStart
STARTED
onResume
RESUMED

Before moving to the next section in this chapter, take some time to compare the output from the app with the flow chart in Figure TBD of the previous chapter.

Creating a Lifecycle Owner

The final task in this chapter is to create a custom lifecycle owner class and demonstrate how to trigger events and modify the lifecycle state from within that class.

Add a new class by right-clicking on the app -> java -> com.ebookfrenzy.lifecycledemo entry in the Project tool window and selecting the New -> Java Class... menu option. Name the class DemoOwner in the Create Java Class dialog before clicking on the OK button. With the new DemoOwner.java file loaded into the code editor, modify it as follows:

package com.ebookfrenzy.lifecycledemo;

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;

public class DemoOwner implements LifecycleOwner {
}

The class is going to need a LifecycleRegistry instance initialized with a reference to itself, and a getLifecycle() method configured to return the LifecycleRegistry instance. Declare a variable to store the LifecycleRegistry reference, a constructor to initialize the LifecycleRegistry instance and add the getLifecycle() method:

public class DemoOwner implements LifecycleOwner {

    private LifecycleRegistry lifecycleRegistry;

    public DemoOwner() {
        lifecycleRegistry = new LifecycleRegistry(this);
    }

    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

Next, the class will need to notify the registry of lifecycle state changes. This can be achieved either by marking the state with the markState() method of the LifecycleRegistry object, or by triggering lifecycle events using the handleLifecycleEvent() method. What constitutes a state change within a custom class will depend on the purpose of the class. For this example, we will add some methods that simply trigger lifecycle events when called:

.
.
private LifecycleRegistry lifecycleRegistry;
.
.
    public void startOwner() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
    }

    public void stopOwner() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
    }

    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
.
.

The final change within the DemoOwner class is to add the DemoObserver class as the observer. This call will be made within the constructor as follows:

public DemoOwner() {
    lifecycleRegistry = new LifecycleRegistry(this);
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    getLifecycle().addObserver(new DemoObserver());
}

Load the MainFragment.java file into the code editor, locate the onActivityCreated() method and add code to create an instance of the DemoOwner class and to call the startOwner() and stopOwner() methods. Note also that the call to add the DemoObserver as an observer has been removed. Although a single observer can be used with multiple owners, it is removed in this case to avoid duplicated and confusing output within the Logcat tool window:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);

    demoOwner = new DemoOwner();
    demoOwner.startOwner();
    demoOwner.stopOwner();
    getLifecycle().addObserver(new DemoObserver());
}

Testing the Custom Lifecycle Owner

Build and run the app one final time, refer to the Logcat tool window and confirm that the observer detected the create, start and stop lifecycle event in the following order:

onCreate
CREATED
onStart
STARTED
onStop
CREATED

Note that the “created” state changes were triggered even though code was not added to the DemoOwner class to do this manually. In fact, these were triggered automatically both when the owner instance was first created and subsequently when the ON_STOP event was handled.

Summary

This chapter has provided a practical demonstration of the steps in involved in implementing lifecycle awareness within and Android app. This included the creation of a lifecycle observer and the design and implementation of a basic lifecycle owner class.