Background execution limits, which you encounter when an application is in the background while a task takes a long time to run, have been added since Android 8.0. We encourage developers to make behavioral changes to improve the user experience across the platform.
To make it easier to adapt to different usage scenarios, we improved the developer experience in following background task constraints by adding features to WorkManager.
We recommend using WorkManager for long-running tasks that need to be executed immediately.
Read this article to learn about the benefits of long-running, immediate-execution tasks handled through WorkManager and how to configure them.
The API is introduced
Since WorkManager version 2.3.0, every Worker can invoke methods in the foreground service. ListenableWorker, as the base class of Worker, provides a new setForegroundAsync() function.
This article takes CoroutineWorker as an example. In CoroutineWorker, setForegroundAsync() is encapsulated in a suspended setForeground() function. This class also provides the pending doWork function, which allows code to run off the main thread. However, everything in this article also applies to other Worker class related functions.
When setForeground(Async) is called, the scheduled task will be executed immediately in the foreground service once the constraints are met. In addition, the WorkManager takes care of the life cycle of the service. The tasks running in the Worker of the foreground service will not be limited by the 10-minute limit of the background tasks.
Start with immediate execution
Let’s look at how to get an existing worker to perform tasks in the foreground service.
Let’s start with a very simple doWork() function. The code executes asynchronously, and results are returned regardless of success or failure.
/* Copyright 2020 Google LLC.spdx-license-Identifier: Apache-2.0 */
override suspend fun doWork(a): Result {
try {
// The code to execute
return Result.success()
} catch (throwable: Throwable) {
// Clean up and output
return Result.failure()
}
}
Copy the code
In doWork(), you also need to tell the WorkManager that the task should be performed immediately in the foreground service.
To do this, you need to create a ForegroundInfo object as the setForeground() argument. ForegroundInfo takes two parameters, one is the Notification ID and the other is the Notification that will be displayed.
When the constraints are met, the following information can be used to create and run the foreground service.
Create ForegroundInfo
Creating ForegroundInfo correctly requires three steps:
- Create a Notification
- Create a Notification Channel
- Introduce notifications to ForegroundInfo
In the following code, createForegroundInfo() calls createForegroundInfo(), and the createNotification() function populates the Notification and creates the corresponding channel.
/* Copyright 2020 Google LLC.spdx-license-Identifier: Apache-2.0 */
/** * create ForegroundInfo */ for foreground server running Worker
private fun createForegroundInfo(a): ForegroundInfo {
// Each Notification needs a different ID
val notificationId = 1
return ForegroundInfo(notificationId, createNotification())
}
/** * Create Notification and required channel for foreground service run task (Andrid O version above) */
private fun createNotification(a): Notification {
//PendingIntent Can be used to cancel the Worker
val intent = WorkManager.getInstance(context).createCancelPendingIntent(id)
val builder = Builder(context, channelId)
.setContentTitle(title)
.setTicker(title)
.setSmallIcon(R.drawable.baseline_gradient)
.setOngoing(true)
.addAction(drawable.ic_delete, cancel, intent)
if (VERSION.SDK_INT >= VERSION_CODES.O) {
createNotificationChannel(channelId, name).also {
builder.setChannelId(it.id)
}
}
return builder.build()
}
/** * Create the required Notification channel */ for Android O and later devices
@TargetApi(VERSION_CODES.O)
private fun createNotificationChannel(
channelId: String,
name: String
): NotificationChannel {
return NotificationChannel(
channelId, name, NotificationManager.IMPORTANCE_LOW
).also { channel ->
notificationManager.createNotificationChannel(channel)
}
}
Copy the code
Perform tasks in front desk service
Now put it all together. Now that we have implemented the doWork function, we can call setForeground() and pass the information we need by calling createForegroundInfo().
/* Copyright 2020 Google LLC.spdx-license-Identifier: Apache-2.0 */
override suspend fun doWork(a): Result {
try {
setForeground(createForegroundInfo())
// The code to execute
return Result.success(workDataOf(KEY_RESULT to result))
} catch (throwable: Throwable) {
// Clean up and print logs
return Result.failure()
}
}
Copy the code
⚠ ️ ⚠ ️ ⚠ ️
SetForeground () is called before the long running task begins.
Otherwise, your Worker will be regarded as non-foreground service until setForeground() is called, which may cause this task to be canceled or other undesired results.
⚠ ️ ⚠ ️ ⚠ ️
The next step
Now that you know when and how to take advantage of long-running workers, you can take the next step and start implementing them in your app. For more information, see the following resources:
View the WorkManager sample code on GitHub:
For the code to perform tasks in the foreground service, see:
- BaseFilterWorker class
- Submit the record
For a detailed guide to running worker and foreground services for long periods of time, and for more information on topics, see:
- Advanced Guide to WorkManager | Support for long-running workers
- Background Processing Guide
- Kotlin coroutine on Android
The WorkManager series helps you understand the features of WorkManager, from basic to advanced:
- Android Jetpack WorkManager | Android Chinese teaching video
- WorkManager practices in Kotlin
- WorkManager: periodic task
- Custom WorkManager – Basic concepts
- Customize the WorkManager with the Dagger
Google IssueTracker submits any issues we encounter, which will help us optimize features and fix bugs first.