37.
An Android Jetpack
Lifecycle Awareness Tutorial
The previous chapter provided an overview of lifecycle awareness and outlined the key classes and interfaces that make this possible within 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.
37.1
Creating the Example Lifecycle Project
Select the Start a new Android Studio project
quick start option from the welcome screen and, within the resulting new project dialog, choose the Add No Activity template before clicking on the Next button.
Enter LifecycleDemo
into the Name field and specify com.ebookfrenzy.lifecycledemo
as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo) and the Language menu to Java.
Within the Project tool window, locate the app -> java -> com.ebookfrenzy. lifecycledemo
entry, right-click on it and select the New -> Activity -> Fragment + ViewModel
option from the resulting menu. In the New Android Activity dialog, enable the Launcher Activity option, change the source language to Java and click on the Finish button to create the activity class with the remaining fields set to the default values.
37.2
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 in 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 android.arch.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 class as outlined in the following listing:
package com.ebookfrenzy.lifecycledemo;
import android.util.Log;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.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.
37.3
Adding the Observer
Observers are added to lifecycle owners via calls to the addObserver()
method of the 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:
.
.
import com.ebookfrenzy.lifecycledemo.DemoObserver;
.
.
@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.
37.4
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
“Android Activity State Changes by Example”
, 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 36-2
of the previous chapter.
37.5
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 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 android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.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);
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:
.
.
import com.ebookfrenzy.lifecycledemo.DemoOwner;
.
.
private DemoOwner demoOwner;
.
.
@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());
}
37.6
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 events 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.
37.7
Summary
This chapter has provided a practical demonstration of the steps involved in implementing lifecycle awareness within an Android app. This included the creation of a lifecycle observer and the design and implementation of a basic lifecycle owner class.