In this article, we will learn how to schedule tasks using WorkManager.
WorkManager is a part of Jetpack Components which provides the facility to execute or perform background operations seamlessly.
Benefits of WorkManager:
1 2 |
// Kotlin + coroutines implementation("androidx.work:work-runtime-ktx:2.9.0") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
package com.androhub.workmanagerexample import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.BackoffPolicy import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingWorkPolicy import androidx.work.ForegroundInfo import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import java.util.concurrent.TimeUnit class MyWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { companion object { private val TAG = MyWorker::class.java.simpleName private const val NOTIFICATION_CHANNEL_ID = "my_worker_notification_id" private const val NOTIFICATION_ID = 324 private val constraints = Constraints.Builder() // WIFI network constraint .setRequiredNetworkType(NetworkType.UNMETERED) .build() // one time work request val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>() .setConstraints(constraints) // backoff criteria for retrying the worker .setBackoffCriteria( BackoffPolicy.EXPONENTIAL, // minimum 10 seconds required 10, TimeUnit.SECONDS ) .addTag(TAG) // pass data to a worker .setInputData(workDataOf("data" to "My name goes here")) // initial delay to execute the worker //.setInitialDelay(5, TimeUnit.SECONDS) .build() // periodic work request to execute work periodically val periodicWorkRequest = PeriodicWorkRequestBuilder<MyWorker>( 15 //minimum 15 minutes is required , TimeUnit.MINUTES) .setConstraints(constraints) .addTag(TAG) //.setInitialDelay(5, TimeUnit.SECONDS) .build() fun executeOneTimeWorker(context: Context) { WorkManager.getInstance(context) .enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, oneTimeWorkRequest) } fun cancelOneTimeWorker(context: Context) { WorkManager.getInstance(context) .cancelWorkById(oneTimeWorkRequest.id) } fun executePeriodicWorker(context: Context) { WorkManager.getInstance(context) .enqueueUniquePeriodicWork( TAG, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest ) } } override fun doWork(): Result { // get the input data passed val data = inputData.getString("data") Log.d(TAG, "Input Data: $data") Log.d(TAG, "Do Work before sleep") Thread.sleep(5000L) Log.d(TAG, "Do Work after sleep") // success result -> can be failure and retry also return Result.success() } override fun getForegroundInfo(): ForegroundInfo { return ForegroundInfo(NOTIFICATION_ID, createNotification()) } private fun createNotification(): Notification { createNotificationChannel() return NotificationCompat .Builder(applicationContext, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(applicationContext.getString(R.string.app_name)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true) .build() } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = applicationContext.getString(R.string.channel_name) val descriptionText = applicationContext.getString(R.string.channel_description) val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance).apply { description = descriptionText } // Register the channel with the system val notificationManager: NotificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } } |
1 2 |
// one time work request val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build() |
1 2 3 |
// periodic work request to execute work periodically val periodicWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(15 //minimum 15 minutes is required , TimeUnit.MINUTES).build() |
1 2 3 4 5 6 7 8 9 |
private val constraints = Constraints.Builder() // WIFI network constraint .setRequiredNetworkType(NetworkType.UNMETERED) .build() // one time work request val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>() .setConstraints(constraints) .build() |
1 2 3 4 5 6 7 8 9 10 11 |
val oneTimeWorkRequest = OneTimeWorkRequestBuilder<MyWorker>() .setConstraints(constraints) .setInputData(workDataOf("data" to "My name goes here")) .build() override fun doWork(): Result { // get the input data passed val data = inputData.getString("data") Log.d(TAG, "Input Data: $data") return Result.success() } |
1 |
WorkManager.getInstance(context).cancelWorkById(uuid) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Composable fun MyApp(modifier: Modifier = Modifier) { val context = LocalContext.current Column { ElevatedButton(onClick = { MyWorker.executeOneTimeWorker(context) }) { Text(text = "Start One Time Worker") } ElevatedButton(onClick = { MyWorker.cancelOneTimeWorker(context) }) { Text(text = "Cancel One Time Worker") } ElevatedButton(onClick = { MyWorker.executePeriodicWorker(context) }) { Text(text = "Start Periodic Worker") } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
package com.androhub.workmanagerexample import android.os.Bundle import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.ElevatedButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.lifecycleScope import androidx.work.WorkInfo import androidx.work.WorkManager import com.androhub.workmanagerexample.ui.theme.WorkManagerExampleTheme import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { WorkManagerExampleTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MyApp() } } } listenWorkerUpdate() } private fun listenWorkerUpdate() { lifecycleScope.launch { WorkManager.getInstance(this@MainActivity) .getWorkInfoByIdFlow(MyWorker.oneTimeWorkRequest.id) .collect { info -> if (info?.state == WorkInfo.State.RUNNING) { Toast.makeText(this@MainActivity, "Work is Running", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.SUCCEEDED) { Toast.makeText(this@MainActivity, "Work is Completed", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.CANCELLED) { Toast.makeText(this@MainActivity, "Work is Cancelled", Toast.LENGTH_SHORT) .show() } } WorkManager.getInstance(this@MainActivity) .getWorkInfoByIdFlow(MyWorker.periodicWorkRequest.id) .collect { info -> if (info?.state == WorkInfo.State.RUNNING) { Toast.makeText(this@MainActivity, "Work is Running", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.SUCCEEDED) { Toast.makeText(this@MainActivity, "Work is Completed", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.CANCELLED) { Toast.makeText(this@MainActivity, "Work is Cancelled", Toast.LENGTH_SHORT) .show() } } } } } @Composable fun MyApp(modifier: Modifier = Modifier) { val context = LocalContext.current Column { ElevatedButton(onClick = { MyWorker.executeOneTimeWorker(context) }) { Text(text = "Start One Time Worker") } ElevatedButton(onClick = { MyWorker.cancelOneTimeWorker(context) }) { Text(text = "Cancel One Time Worker") } ElevatedButton(onClick = { MyWorker.executePeriodicWorker(context) }) { Text(text = "Start Periodic Worker") } } } @Preview(showBackground = true) @Composable fun MyAppPreview() { WorkManagerExampleTheme { MyApp() } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
private fun listenWorkerUpdate() { lifecycleScope.launch { WorkManager.getInstance(this@MainActivity) .getWorkInfoByIdFlow(MyWorker.oneTimeWorkRequest.id) .collect { info -> if (info?.state == WorkInfo.State.RUNNING) { Toast.makeText(this@MainActivity, "Work is Running", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.SUCCEEDED) { Toast.makeText(this@MainActivity, "Work is Completed", Toast.LENGTH_SHORT) .show() } else if (info?.state == WorkInfo.State.CANCELLED) { Toast.makeText(this@MainActivity, "Work is Cancelled", Toast.LENGTH_SHORT) .show() } } } } |
Happy Coding
Subscribe to us and get the latest news.