Blog Infos
Author
Published
Topics
, , , ,
Published

The Android lifecycle is important in managing the lifespan of various Android components, such as Activities, Fragments, Views, and Services, from their creation to their destruction. Understanding the Android lifecycle is key to correctly initializing objects and releasing resources at the appropriate time, ensuring proper memory management.

Android provides dedicated callback methods like onCreate()onStart(), and onDestroy() that are triggered at specific points in a component’s lifecycle. These methods allow developers to manage objects and resources effectively, handling tasks like initialization, behavior changes depending on app state (such as background processes or configuration changes), and releasing unused memory to optimize app performance.

As the Android ecosystem evolves, so do the development practices. Over time, many developers have shifted from Java to Kotlin, adopting more Kotlin-based tech stacks like Coroutines and Flow for handling asynchronous operations. However, the traditional Android components were initially built around callback systems, which can make seamless handling of coroutine lifecycles challenging.

To address this, AndroidX libraries introduced androidx-activity and androidx-fragment extensions, which implement LifecycleOwner and provide lifecycle-aware APIs like lifecycleScope and repeatOnLifecycle(). These APIs allow developers to safely manage lifecycle-aware objects or coroutine scopes according to the Android lifecycle, ensuring coroutines are launched and canceled at the right time. Additionally, AndroidX includes a lifecycle library for Services, specifically LifecycleService, to handle coroutine scopes conveniently in Service components.

In this article, you’ll explore LifecycleService and how you can apply similar approaches to manage the resource lifecycle in your FirebaseMessagingService, leveraging Kotlin coroutines for more efficient, modern code practices.

Understanding the Service Lifecycle

Services in Android have key callback methods that handle important aspects of their lifecycle and, if needed, allow other components to bind to the service. Here are the main callback methods you should override:

  • onStartCommand(): Called when another component (e.g., an activity) starts the service using startService(). The service can run indefinitely in the background. It’s your responsibility to stop the service using stopSelf() or stopService() once its work is done. If your service only provides binding, you don’t need to implement this method.
  • onBind(): Called when another component binds to the service using bindService(). This method must return an IBinder to let clients communicate with the service. If you don’t allow binding, return null. You must always implement this method if binding is required.
  • onCreate(): Called when the service is first created. Use this for one-time setup before onStartCommand() or onBind() is called. If the service is already running, this method won’t be invoked again.
  • onDestroy(): Called when the service is no longer in use and is about to be destroyed. Implement this method to clean up resources (threads, listeners, receivers). This is the last callback the service receives.

These methods ensure that your service runs smoothly and properly handles cleanup when it’s no longer needed. So you can imagine the lifecycle instance of the LifecycleService provided by LifecycleOwner should align with the stages outlined in the illustration below:

Since services often run in the background, failing to properly cancel coroutine tasks and release memory can lead to long-running background processes that occupy memory unnecessarily and are difficult to debug. Let’s explore how to manage this effectively using LifecycleService.

LifecycleService

The LifecycleService is provided by the lifecycle-service library, which is a Service that simply extends the LifecycleOwner so that you can manage your coroutine scope efficiently. To start using LifecycleService, add the following dependency to your Gradle file:

dependencies {
implementation("androidx.lifecycle:lifecycle-service:2.8.5")
}

Now, you can create your own service by extending LifecycleService, allowing you to take advantage of lifecycleScope. This will automatically cancel any launched coroutine jobs safely as the lifecycle progresses, as demonstrated below:

class MyService: LifecycleService() {
override fun onCreate() {
super.onCreate()
lifecycleScope.launch {
// ..
}
}
}
view raw my_service.kt hosted with ❤ by GitHub

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Unclutter your Jetpack – an opinionated look at Googles library collection

Remember when Google hit the Reset button to clean up the mess of its Android support libraries? Since then, a ton of new Jetpack libraries have been created. Some are obvious choices, like compose.
Watch Video

Unclutter your Jetpack - an opinionated look at Googles library collection

Thomas Künneth
Senior Android Developer
snappmobile_io

Unclutter your Jetpack - an opinionated look at Googles library collection

Thomas Künneth
Senior Android Devel ...
snappmobile_io

Unclutter your Jetpack - an opinionated look at Googles library collection

Thomas Künneth
Senior Android Developer
snappmobile_io

Jobs

It’s important to note that you don’t need to use the Lifecycle.repeatOnLifecycle API in services. This API was primarily designed to enable safer Flow collection in the UI layer of Android apps. Its restartable behavior is tailored for view-based components like Activities and Fragments, ensuring that processing occurs only when the UI is visible. Since services operate in the background and don’t have a UI, repeatOnLifecycle isn’t necessary in this context.

FirebaseMessagingService

In modern mobile services and startups, increasing user engagement by delivering timely, relevant information is crucial. Most applications achieve this by leveraging push notifications to keep users informed at the right moment.

Firebase offers a cloud messaging service called Firebase Cloud Messaging (FCM), which allows you to seamlessly implement push notifications. The official documentation provides guidance on creating your own service by extending the FirebaseMessagingService, as demonstrated in the code below:

class MyService: FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// FCM registration token to your app server.
sendRegistrationToServer(token)
}
}
view raw my_service.kt hosted with ❤ by GitHub

You may have encountered a situation where you need to register a user token with your backend server. In such cases, you should override the onNewToken method and ensure that the required tasks are executed on a background thread. If you’re using coroutines for asynchronous tasks, it’s crucial to manage the coroutine scope’s lifecycle properly, ensuring jobs are canceled correctly in line with the service’s lifecycle.

If you examine the implementation of FirebaseMessagingService in the open-source Firebase project on GitHub, you’ll notice it extends Service rather than LifecycleService. As a result, you’ll need to manually manage your coroutine scope to ensure it aligns with the service’s lifecycle.

Firebase Messaging Lifecycle KTX

To address the lifecycle management issues mentioned earlier, you can leverage the firebase-android-ktx library. The firebase-android-ktx library offers Kotlin and Jetpack Compose-friendly Firebase extensions, helping you focus on business logic. While it includes extensions for Realtime Database, this article will focus on the Firebase Messaging Lifecycle KTX library.

The Firebase Messaging Lifecycle KTX extension enables you to implement a lifecycle-aware FirebaseMessagingService, allowing you to manage and cancel coroutine scopes according to the service’s lifecycle. This is especially useful when refreshing and sending a token to the server in the onNewToken method, ensuring the coroutine scope is properly handled and preventing memory leaks by stopping tasks once the service is no longer active. To use this library, you should override

To use this library, add the dependency below to your module’s build.gradle.kts file:

dependencies {
implementation("com.github.skydoves:firebase-messaging-lifecycle-ktx:0.2.0")
}

Now, you can simply use LifecycleAwareFirebaseMessagingService instead of FirebaseMessagingService. This lifecycle-aware version is designed to manage tasks in alignment with the service’s lifecycle. For example, in the onNewToken method, you can send a token to your backend using the lifecycleScope.launch function. This ensures that the coroutine scope is automatically canceled when the service’s lifecycle changes, preventing any unintended background tasks from continuing to run.

class AppFirebaseMessagingService : LifecycleAwareFirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
lifecycleScope.launch {
// send the token to the server
..
}
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
Log.d(APP_LOG_TAG, "FCMService#onMessageRec onMessageReceived: ${message.data}")
}
override fun onDestroy() {
super.onDestroy()
Log.d(APP_LOG_TAG, "FCMService#onDestroy onDestroy")
}
}

As shown in the example above, LifecycleAwareFirebaseMessagingService provides a lifecycleScope, ensuring that any coroutine tasks launched within this scope are automatically canceled when the service’s lifecycle reaches the onDestroy state. This helps prevent resource leaks and ensures proper task management.

Conclusion

In this article, you’ve explored the lifecycles of Service and LifecycleService, focusing on efficiently managing tasks, especially when running coroutine jobs. We also discussed how to apply a similar approach to FirebaseMessagingService. Properly canceling tasks that no longer need to run is crucial for preventing memory leaks and avoiding unnecessary memory usage in Android components.

If you have any questions or feedback on this article, you can find the author on Twitter @github_skydoves or GitHub. If you’d like to stay updated with the latest information through articles and references, tips with code samples that demonstrate best practices, and news about the overall Android/Kotlin ecosystem, check out ‘Learn Kotlin and Android With Dove Letter’.

As always, happy coding!

— Jaewoong

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Yes! You heard it right. We’ll try to understand the complete OTP (one time…
READ MORE
blog
Firebase Remote Config is a cloud service that lets you change the behavior and…
READ MORE
blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
Menu