Lifecycle-Aware Components Using Android Jetpack
- Getting Started
- Registering for the spoonacular API
- Lifecycles in Android
- Reacting to Lifecycle Changes
- Using Lifecycle-Aware Components
- Creating a Lifecycle Observer
- Lifecycle Events and States
- Reacting to Lifecycle Events
- Using Lifecycle States
- Subscribing to Lifecycle Events
- Who Owns the Lifecycle?
- Using ProcessLifecycleOwner
- Creating a Custom Lifecycle Owner
- Adding Events
- Reacting to Events
- Testing a Lifecycle-Aware Component
- Setting Up The Tests
- Adding The Tests
- LiveData: A Lifecycle-Aware Component
- Creating and Assigning LiveData Variables
- Observing LiveData Changes
- Where to Go From Here?
Android Jetpack is a collection of libraries that help developers improve their code, reduce boilerplate code and make their app work consistently across different Android versions. Jetpack’s Android Architecture Components provide tools to help implement lifecycle-aware components that react to lifecycle changes in activities or fragments.
In this tutorial, you’ll create a lifecycle-aware component in an app named AwarenessFood. This component will improve how the app handles network connection changes. You’ll also create a lifecycle owner that will communicate the network state to the activity.
The app shows a random recipe to the user and has two menu options: one to get a new random recipe and the other to show some food-related trivia. When the device is offline, the main screen shows a snackbar with a message and a retry option.
Over the course of this tutorial, you’ll learn about:
- Lifecycles in Android
- Lifecycle-aware components
- Lifecycle observers
- Events and states
- Lifecycle owners
- How to test your lifecycle-aware components
Download the materials using the Download Materials button at the top or bottom of this tutorial. Open Android Studio 4.2.1 or later and import the starter project.
Below is a summary of what each package does:
- analytics: Contains classes for tracking app events.
- data: Contains model classes.
- di: You’ll find classes for providing dependency injection here.
- monitor: Contains a single class for observing network connectivity.
- network: Here, you’ll find classes for accessing external APIs.
- repositories: Has classes for managing persistence.
- viewmodels: Contains business logic classes.
- views: Holds a custom View.
Registering for the spoonacular API
AwarenessFood uses the spoonacular API to fetch recipes. You’ll need to register for this API to be able to run the app successfully.
Go to the spoonacular website and create a new account. Once you confirm your account, log in and go to your profile to find your API key. Copy it, open RecipesModule.kt inside the di package and replace the value in the following line:
private const val API_KEY = "YOUR_API_KEY_HERE"
Build and run. You’ll see a screen with a random recipe, similar to the one shown below. Bonus points if you get the same recipe as in the image. :]
To get another random recipe, press the Reload button in the action bar. If you try to get a new recipe and your device goes offline, you’ll see a snackbar with the error message and retry button, as shown below:
To go to the food trivia screen, press the Food Trivia option in the More menu. You’ll implement this functionality later in the tutorial. Right now, you’ll only see a button to get the food trivia, as shown below:
This completes the setup you need to run the app. Now, you’re ready to learn about lifecycle-aware components.
Lifecycles in Android
An important basic concept you need to understand as an Android developer is how the lifecycle of activities and fragments work. The lifecycle is a series of callbacks executed in a certain order when the status of the activity or fragment changes.
The lifecycle is important because certain actions need to take place when the activity or fragment is in a specific state. For example, setting the activity layout needs to take place in its
In a fragment, you need to create the view and set its layout in
onCreateView(). Another example is enabling the current location reading in
For the destruction process, the location reading should stop in
onStop(), which is also where you need to unregister other components. It’s important to know that not all the callbacks get executed every time. For example, the operating system may or may not execute
The following diagram shows the complete lifecycle for activities:
If you want to know more about activity’s lifecycle, go to our Introduction to Android Activities With Kotlin tutorial.
This diagram shows the lifecycle for fragments:
If you want to know more about a fragment’s lifecycle, read Android Fragments Tutorial: An Introduction With Kotlin.
Reacting to Lifecycle Changes
Most apps have multiple components that need to react to the lifecycle of an activity or fragment. You need to initialize or register these components in
onStart() and unregister or perform some cleanup in
onStop(). In some cases, you need to do some other actions during another lifecycle callback.
Following this pattern, your code can become messy and error-prone. The code within
onStop() will expand indefinitely. Meanwhile, it’s easy to forget to unregister some of your components or to call the component’s methods in the wrong lifecycle callback, causing bugs, memory leaks and crashes.
You can see some of these problems in the app right now. Open NetworkMonitor.kt in the starter project. This class is in charge of listening to the state of the network connection and notifying the activity if the state of the connection changes.
NetworkMonitor instance needs to be available from the initialization of the activity. Open MainActivity.kt, which contains the code to initialize it in the
onCreate() by calling
NetworkMonitor then registers the network callbacks in
onStart() by calling
networkMonitor.registerNetworkCallback(). Finally, it unregisters these callbacks in
onStop() by calling
Initializing the component, registering the callbacks and unregistering them again add a lot of boilerplate code to the activity. Besides, you only need to add
onStop() to call the
MainActivity, there’s only one component that needs to react to lifecycle changes. However, in a bigger and more complex app, several components need to do the same, it can become a complete mess.
Using Lifecycle-Aware Components
NetworkMonitor performs different actions that depend on the state of the lifecycle of the activity where it lives. In other words, NetworkMonitor needs to be a lifecycle-aware component and react to changes in the lifecycle of its parent — in this case,
Jetpack provides classes and interfaces to help create lifecycle-aware components. By using them, you can improve the way
NetworkMonitor performs its actions. These actions will execute automatically according to the current lifecycle state of its parent activity.
A lifecycle owner is a component that has a lifecycle, like an activity or a fragment. The lifecycle owner needs to know all the components that need to listen for its lifecycle changes. Using the observer pattern is the best approach to achieve this.