Introduction to WorkManager
WorkManager is a Google component designed to solve the problem of running tasks even after an application exits or the device restarts.
How to manage background work
WorkManager automatically selects the scheduling service for underlying jobs based on the API level of the device. The previous official figure below clearly illustrates the scheduling service selected by WorkManager for each version of the API. Currently, API 14 is supported at least
And using threads directly in your application
First of all, WorkManager is not meant to replace the work of threads in Android. Google defines background tasks in its official documentation
Google divides background Tasks into four types: Immediate Task, Exact Task, Expedited Task, and Deferred Task
Immediate Task
Tasks that need to be completed while the user is operating the APP can be classified as Imeediate tasks. It is recommended that you use Kotlin coroutines or Java threads to perform tasks in your APP
Exact Task
Tasks that need to run at Exact times can be classified as Exact tasks. AlarmManager is recommended
Expedited Task & Deferred Task
In addition to the above scenarios, a Task can be categorized as an Expedited Task if it needs to start as soon as possible, or as a Deferred Task if it does not. WorkManager is recommended
Starting with WorkManager 2.7.0, you can use setExpedited() to declare workers as urgent tasks. Corresponding to the Expedited Task above, you need to override the getForegroundInfoAsync method in the Worker as well
OneTimeWorkRequestBuilder<T>().apply {
setInputData(inputData)
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}.build()
Copy the code
Use of WorkManager
Worker & CoroutineWorker
The Worker class is used to define the work performed by the task. Inheriting the Worker class writes tasks that need to be performed in the doWork() method (CoroutineWorker can be used if you want to use a Kotlin coroutine
The return value of the doWork() method notifies WorkManager of the result of the task execution
Result.success()
Task Executed SuccessfullyResult.failure()
Task execution failureResult.retry()
The task needs to be executed again
@WorkerThread
public abstract @NonNull Result doWork(a);
Copy the code
WorkRequests
The function of WorkRequest class is to define the running mode of Worker (for example, constraints that Worker needs to meet for running, data transfer for Worker, Worker scheduling information configuration, etc.). WorkManager provides two implementations of WorkRequest
- OneTimeWorkRequest(One-time work)
- PeriodicWorkRequest(Periodic work)
// The argument passed to the Worker
val data = Data.Builder().putString(DownloadWorker.KEY_NAME, downloadContent).build()
// Constraints on Worker execution
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
/ / create WorkRequest
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
.build()
Copy the code
WorkManager
WorkManager is used to manage Work, such as adding tasks, canceling tasks, and listening to the execution of tasks
Join the task
mWorkManager.beginUniqueWork(
DownloadWorker.TAG,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
// This setting requires overwriting getForegroundInfo in the Worker
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
).enqueue()
Copy the code
Monitor Worker execution
mWorkManager.getWorkInfosByTagLiveData(DownloadWorker.TAG)
.observe(this) {
if (it.isNotEmpty()) {
val workInfo = it[0]
when(workInfo.state) {
WorkInfo.State.SUCCEEDED,
WorkInfo.State.BLOCKED,
WorkInfo.State.ENQUEUED,
WorkInfo.State.RUNNING,
WorkInfo.State.CANCELLED
}
}
}
Copy the code
The obtained task result is List
, where List[0] indicates the latest
Practice 3.
Create Worker to simulate task execution
class DownloadWorker : CoroutineWorker {
private lateinit var mNotificationBuilder: NotificationCompat.Builder
constructor(appContext: Context, params: WorkerParameters) : super(appContext, params)
//Worker executes the task
override suspend fun doWork(a): Result {
val data = fakeDownload()
showSuccessNotification()
val outData = Data.Builder().putString(OUTPUT_KEY, data).build()
return Result.success(outData)
}
// Create ForegroundInfo Worker which will run as foreground service
override suspend fun getForegroundInfo(a): ForegroundInfo {
val context = applicationContext
return ForegroundInfo(
START_DOWNLOAD_NOTIFICATION_ID,
createNotification(context) {
setContentTitle("Start Download")
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentText("Start Download ${inputData.getString(INPUT_KEY)}")
priority = NotificationCompat.PRIORITY_DEFAULT
val cancel = WorkManager.getInstance(context).createCancelPendingIntent(id)
// Set the cancelWork button
addAction(R.drawable.icon_cancel, "Cancel", cancel)
mNotificationBuilder = this})}private suspend fun fakeDownload(a): String {
Log.i(TAG, "Thread:${Thread.currentThread().name}")
for (i in 0.100.) {
delay(100L)
mNotificationBuilder.setContentText("Start Download ${inputData.getString(INPUT_KEY)} $i%")
notifyNotification(applicationContext, START_DOWNLOAD_NOTIFICATION_ID, mNotificationBuilder.build())
}
Log.i(TAG, "Download Succeed")
return "Download Succeed"
}
private fun showSuccessNotification(a) {
notifyNotification(applicationContext, DOWNLOAD_SUCCEED_NOTIFICATION_ID) {
setContentTitle("Download Succeed")
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentText("Download ${inputData.getString(INPUT_KEY)} Succeed")
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
val intent = Intent(applicationContext, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE)
setContentIntent(pendingIntent)
}
}
}
Copy the code
Create WorkRequest
Create WorkRequest set execution conditions, pass parameters, and queue the WorkRequest
private fun enqueueDownloadWork(a) {
val downloadContent = The Avengers
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val data = Data.Builder().putString(DownloadWorker.INPUT_KEY, downloadContent).build()
mWorkManager.beginUniqueWork(
DownloadWorker.TAG,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)//
.build()
).enqueue()
}
Copy the code
Monitor the execution of tasks
Monitor the Worker’s execution through the TAG set when joining the task
mWorkManager.getWorkInfosByTagLiveData(DownloadWorker.TAG)
.observe(this) {
if (it.isNotEmpty()) {
val workInfo = it[0]
when(workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
mBinding.download.setText("Download again")
mBinding.tips.visibility = View.VISIBLE
mBinding.tips.text = workInfo.outputData.getString(DownloadWorker.OUTPUT_KEY)
mBinding.download.setOnClickListener {
enqueueDownloadWork()
}
}
WorkInfo.State.BLOCKED,
WorkInfo.State.ENQUEUED,
WorkInfo.State.RUNNING, -> {
mBinding.tips.text = "Downloading"
mBinding.download.text = "Cancel download"mBinding.download.setOnClickListener { mWorkManager.cancelUniqueWork(DownloadWorker.TAG) } } WorkInfo.State.CANCELLED -> { mBinding.tips.visibility = View.GONE mBinding.download.text ="Start downloading"
mBinding.download.setOnClickListener {
enqueueDownloadWork()
}
}
}
}
}
Copy the code
Full Project Address
GitHub – Mao0509/WorkManagerDemo
4. Reference
Define work requests|Android Developers