Aller au contenu principal

WorkManager in Android - Simplifying Background Tasks

Have you ever found yourself in a situation where you needed to perform a task in the background of your Android app, but weren't sure which approach to take? You're not alone. Android developers have long struggled with the best way to handle background tasks, whether it's running a periodic sync operation, uploading a file to a server, or sending a push notification.

Fortunately, Google introduced WorkManager in Android Architecture Components, which simplifies the process of handling background tasks in Android. In this article, we'll take a closer look at what WorkManager is and how to use it.

What is WorkManager?

WorkManager is a library that allows you to schedule background tasks that can be deferred to a later time if they are interrupted. It automatically chooses the best way to schedule a task based on factors such as device API level, available resources, and battery status. WorkManager also provides an easy-to-use API for defining and managing tasks, as well as observing their state and progress.

How to Use WorkManager

Using WorkManager is straightforward and can be broken down into three main steps:

  1. Define the task to be performed in a Worker class.
  2. Create a WorkRequest object that represents the task and set any desired constraints.
  3. Enqueue the WorkRequest using the WorkManager API.

Defining a Worker

A Worker is a class that performs a task in the background. It should extend the Worker class and override the doWork() method.

class MyWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {

override fun doWork(): Result {
// Perform the task here
return Result.success()
}
}

The doWork() method should return a Result object indicating the outcome of the task. You can return Result.success() if the task completed successfully, Result.failure() if it failed, or Result.retry() if it should be retried later.

Creating a WorkRequest

A WorkRequest represents a task to be executed by a Worker. You can create a WorkRequest using the OneTimeWorkRequest or PeriodicWorkRequest classes, depending on whether the task should be executed once or repeatedly.

val myWorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build()

val myPeriodicWorkRequest = PeriodicWorkRequestBuilder<MyWorker>(
24, TimeUnit.HOURS
).build()

You can set constraints on a WorkRequest to specify when it should be executed. For example, you can require that the device is plugged in or has a certain level of battery.

val myWorkRequest = OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(
Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()

Enqueueing a WorkRequest

To execute a WorkRequest, you need to enqueue it using the enqueue() method of the WorkManager API.

WorkManager.getInstance(context).enqueue(myWorkRequest)

You can also observe the state and progress of a WorkRequest using the WorkManager API. For example, you can listen for changes to the state of a WorkRequest using a LiveData object.

WorkManager.getInstance(context).getWorkInfoByIdLiveData(myWorkRequest.id)
.observe(this, Observer { workInfo ->
// Handle the change in state
})

Example 1: Kotlin Simple WorkManager Example with Coroutines

Follow the following steps:

Step 1: Install WorkManager

In you app levelbuild.gradle install workmanager by adding it as a gradle dependency using the following implementation statement:

    implementation 'androidx.work:work-runtime-ktx:2.5.0'

Then sync for it to be installed in your project.

Step 2: Create a ForegroundWorker

Create a class called ForegroundWorker and add extend the CoroutineWorker,passing in the context and WorkerParameters as arguments to the class:

class ForegroundWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {

Prepare a notification manager as an instance of this class:

    private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

Override the doWork() function:

    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
setForeground(createForegroundInfo())
return@withContext runCatching {
runTask()
Result.success()
}.getOrElse {
Result.failure()
}
}

Define a function that creates a notification channel:

    @RequiresApi(Build.VERSION_CODES.O)
private fun createChannel(id: String, channelName: String) {
notificationManager.createNotificationChannel(
NotificationChannel(id, channelName, NotificationManager.IMPORTANCE_DEFAULT)
)
}

Create Foregroundinfo:

    //Creates notifications for service
private fun createForegroundInfo(): ForegroundInfo {
val id = "1225"
val channelName = "Downloads Notification"
val title = "Downloading"
val cancel = "Cancel"
val body = "Long running task is running"

val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel(id, channelName)
}

val notification = NotificationCompat.Builder(applicationContext, id)
.setContentTitle(title)
.setTicker(title)
.setContentText(body)
.setSmallIcon(R.drawable.ic_notification)
.setOngoing(true)
.addAction(android.R.drawable.ic_delete, cancel, intent)
.build()

return ForegroundInfo(1, notification)
}

Create a function to represent the task to be performed:

    //Fake long running task for 60 seconds
private suspend fun runTask() {
delay(60000)
}

Now override the doWork() function and invoke the above functions:

    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
setForeground(createForegroundInfo())
return@withContext runCatching {
runTask()
Result.success()
}.getOrElse {
Result.failure()
}
}

Here's the full code for the class:

ForegroundWorker.kt

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.work.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext

class ForegroundWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {

private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
setForeground(createForegroundInfo())
return@withContext runCatching {
runTask()
Result.success()
}.getOrElse {
Result.failure()
}
}

//Fake long running task for 60 seconds
private suspend fun runTask() {
delay(60000)
}

//Creates notifications for service
private fun createForegroundInfo(): ForegroundInfo {
val id = "1225"
val channelName = "Downloads Notification"
val title = "Downloading"
val cancel = "Cancel"
val body = "Long running task is running"

val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel(id, channelName)
}

val notification = NotificationCompat.Builder(applicationContext, id)
.setContentTitle(title)
.setTicker(title)
.setContentText(body)
.setSmallIcon(R.drawable.ic_notification)
.setOngoing(true)
.addAction(android.R.drawable.ic_delete, cancel, intent)
.build()

return ForegroundInfo(1, notification)
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createChannel(id: String, channelName: String) {
notificationManager.createNotificationChannel(
NotificationChannel(id, channelName, NotificationManager.IMPORTANCE_DEFAULT)
)
}
}

Step 3: Schedule work using WorkManager

In your main activity instantiate the workmanager using the getInstance() method, passing in the context:

WorkManager.getInstance(this)

Then begin enqueue work and observer its state:

.beginUniqueWork("ForegroundWorker", ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.from(ForegroundWorker::class.java)).enqueue().state
.observe(this) { state ->
Log.d(TAG, "ForegroundWorker: $state")
}

Here's the full code:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

WorkManager.getInstance(this)
.beginUniqueWork("ForegroundWorker", ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.from(ForegroundWorker::class.java)).enqueue().state
.observe(this) { state ->
Log.d(TAG, "ForegroundWorker: $state")
}
}
}

Reference

Find code reference below:

No.Link
1.Download Code here
2.Browse code here
3.Follow Code Author here

Example 2: Kotlin Android WorkManager + Pending Notification Example

This app will demonstrate workmanager and pending notification usage. Here are the features of the app created:

  1. Backwards compatible up to API 14
  2. Uses JobScheduler on devices with API 23+
  3. Uses a combination of BroadcastReceiver + 4. AlarmManager on devices with API 14-22
  4. Add work constraints like network availability or charging status
  5. Schedule asynchronous one-off or periodic tasks
  6. Monitor and manage scheduled tasks
  7. Chain tasks together
  8. Ensures task execution, even if the app or device restarts
  9. Adheres to power-saving features like Doze mode

Here is the demo of what is created:

Kotlin ANdroid WorkManager Example

Kotlin Android WorkManager with Pending notification

Step 1: Dependencies

In your gradle script make sure you add the following dependency for workmanager:

    implementation "androidx.work:work-runtime-ktx:2.2.0"

Step 2: Design Layout

Now design your xml layout for the main activity as follows:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

<variable
name="viewModel"
type="com.mili.workmanagerandpendingnotification.MainViewModel" />

<variable
name="view"
type="android.view.View" />
</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="8dp"
android:textColor="@android:color/black"
android:text="Please enter time in minutes and minimum value should be 15 " />

<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense">

<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_periodic_time"
android:inputType="number"
android:text="@={viewModel.data}" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_set_period_work"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.setPeriodicWorkButtonClicked()}"
android:text="Set Periodic Work"
android:layout_margin="8dp"
android:visibility="@{viewModel.data.toString().length() > 0 ? (Integer.parseInt(viewModel.data.toString()) > 14 ? view.VISIBLE : view.GONE):view.GONE }" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textColor="@android:color/black"
android:text="OR" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="8dp"
android:textColor="@android:color/black"
android:text="Please Enter Description for one time work" />

<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense">

<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_one_time_work_description"
android:text="@={viewModel.oneTimeWorkData}" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_set_instant_work"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.setOneTimeWorkButtonClicked()}"
android:text="Run One Time Work"
android:layout_margin="8dp"
android:visibility="@{viewModel.oneTimeWorkData.toString().length() > 0 ? view.VISIBLE : view.GONE }"
/>

</LinearLayout>
</layout>

Step 3: Create Constants

Create an object class called constants that will hold for us variables which don't change. For example, such variables may be used to represent channel IDs for periodic work or for one time work etc:

Constants.kt

object Constants {
const val CHANNEL_ID_PERIOD_WORK = "PERIODIC_APP_UPDATES"
const val CHANNEL_ID_ONE_TIME_WORK = "INSTANT_APP_UPDATES"
const val ONETIME_WORK_DESCRIPTION = "ONETIME_WORK_DESCRIPTION"
const val LAST_PERIODIC_TIME = "LAST_PERIODIC_TIME"
}

Step 4: Create SharedPreference Helpers

These helpers will be used to save key/value pairs locally. This function for example writes to shared preferences:

    fun writeToSharedPreferences(context: Context, key: String, @NonNull value:String) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val editor = sharedPreferences.edit()
editor.putString(key,value)
editor.apply()
}

While this one reads from it:

    fun readFromSharedPreferences(context: Context, key: String, @NonNull defaultValue:String) : String? {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return sharedPreferences.getString(key,defaultValue)
}

Here is the full class:

SharedPrefHelpers.kt

import android.content.Context
import android.preference.PreferenceManager
import androidx.annotation.NonNull

object SharedPrefHelpers {

fun readFromSharedPreferences(context: Context, key: String, @NonNull defaultValue:String) : String? {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return sharedPreferences.getString(key,defaultValue)
}

fun writeToSharedPreferences(context: Context, key: String, @NonNull value:String) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val editor = sharedPreferences.edit()
editor.putString(key,value)
editor.apply()
}
}

Step 5: Create a One Time Background Notification

Start by creating a class known as OnetimeBackgroundNotification and make it extend the androidx.work.Worker class:

class OnetimeBackgroundNotification(private val context: Context, private val workerParameters: WorkerParameters) : Worker(context,workerParameters) {
//....

Inside the class create a function to show notification. The following function allows you to show notification:

    private fun showNotification() {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = TaskStackBuilder.create(context).run {
// Add the intent, which inflates the back stack
addNextIntentWithParentStack(intent)
// Get the PendingIntent containing the entire back stack
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

val notification = NotificationCompat.Builder(context, Constants.CHANNEL_ID_ONE_TIME_WORK).apply {
setContentIntent(pendingIntent)
}
notification.setContentTitle("ONE TIME WORK")
notification.setContentText(workerParameters.inputData.getString(ONETIME_WORK_DESCRIPTION))
notification.priority = NotificationCompat.PRIORITY_HIGH
notification.setCategory(NotificationCompat.CATEGORY_ALARM)
notification.setSmallIcon(R.drawable.ic_notifications_none_blac)
val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
notification.setSound(sound)
val vibrate = longArrayOf(0, 100, 200, 300)
notification.setVibrate(vibrate)

with(NotificationManagerCompat.from(context)) {
notify(2, notification.build())
}
}

Override the doWork function and invoke the showNotification() function:

    override fun doWork(): Result {
showNotification()
return Result.success()
}

Here is the full class:

OnetimeBackgroundNotification.kt

import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.mili.workmanagerandpendingnotification.Constants.ONETIME_WORK_DESCRIPTION

class OnetimeBackgroundNotification(private val context: Context, private val workerParameters: WorkerParameters) : Worker(context,workerParameters) {
override fun doWork(): Result {
showNotification()
return Result.success()
}

private fun showNotification() {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = TaskStackBuilder.create(context).run {
// Add the intent, which inflates the back stack
addNextIntentWithParentStack(intent)
// Get the PendingIntent containing the entire back stack
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

val notification = NotificationCompat.Builder(context, Constants.CHANNEL_ID_ONE_TIME_WORK).apply {
setContentIntent(pendingIntent)
}
notification.setContentTitle("ONE TIME WORK")
notification.setContentText(workerParameters.inputData.getString(ONETIME_WORK_DESCRIPTION))
notification.priority = NotificationCompat.PRIORITY_HIGH
notification.setCategory(NotificationCompat.CATEGORY_ALARM)
notification.setSmallIcon(R.drawable.ic_notifications_none_blac)
val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
notification.setSound(sound)
val vibrate = longArrayOf(0, 100, 200, 300)
notification.setVibrate(vibrate)

with(NotificationManagerCompat.from(context)) {
notify(2, notification.build())
}
}
}

Step 6: Create Periodic Notifications

This time you create periodic notifications as opposed to one time notifications. You follow the above pattern: extend the androidx.work.Worker, create a function to show a notification, then invoke it inside the dowWork() method:

PeriodicBackgroundNotification.kt

import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.media.Ringtone
import android.media.RingtoneManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.mili.workmanagerandpendingnotification.Constants.CHANNEL_ID_PERIOD_WORK
import com.mili.workmanagerandpendingnotification.Constants.LAST_PERIODIC_TIME

class PeriodicBackgroundNotification(private val context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
showNotification()
return Result.success()
}

private fun showNotification() {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = TaskStackBuilder.create(context).run {
// Add the intent, which inflates the back stack
addNextIntentWithParentStack(intent)
// Get the PendingIntent containing the entire back stack
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

val notification = NotificationCompat.Builder(context, CHANNEL_ID_PERIOD_WORK).apply {
setContentIntent(pendingIntent)
}
notification.setContentTitle("PERIOD WORK")
notification.setContentText(SharedPrefHelpers.readFromSharedPreferences(context,
LAST_PERIODIC_TIME,"0"))
notification.priority = NotificationCompat.PRIORITY_HIGH
notification.setCategory(NotificationCompat.CATEGORY_ALARM)
notification.setSmallIcon(R.drawable.ic_notifications_none_blac)
val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
notification.setSound(sound)
val vibrate = longArrayOf(0, 100, 200, 300)
notification.setVibrate(vibrate)

with(NotificationManagerCompat.from(context)) {
notify(1, notification.build())
}
}

}

Step 7: Create a ViewModel

The next step is to create the viewmodel class for the main activity:

MainViewModel.kt

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData

class MainViewModel(application: Application) : AndroidViewModel(application) {
val data = MutableLiveData<String>()
val oneTimeWorkData = MutableLiveData<String>()
private val time = MutableLiveData<Int>()
private val oneTimeWorkDescription = MutableLiveData<String>()

fun setPeriodicWorkButtonClicked() {
time.value = data.value?.toInt()
}

fun setPeriodWork() : LiveData<Int> {
return time
}

fun setOneTimeWorkButtonClicked() {
oneTimeWorkDescription.value = oneTimeWorkData.value
}

fun setOneTimeWork() : LiveData<String> {
return oneTimeWorkDescription
}
}

Step 8: Create Application Class

Do this by extending the android.app.Application class:

class App() : Application() {

In it define a function to create a notification channel as follows:

    private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_HIGH
val channelPeriodic = NotificationChannel(CHANNEL_ID_PERIOD_WORK, "Period Work Request", importance)
channelPeriodic.description = "Periodic Work"
val channelInstant = NotificationChannel(CHANNEL_ID_ONE_TIME_WORK, "One Time Work Request", importance)
channelInstant.description = "One Time Work"
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = applicationContext.getSystemService(
NotificationManager::class.java
)
notificationManager!!.createNotificationChannel(channelPeriodic)
notificationManager.createNotificationChannel(channelInstant)
}
}

Override the onCreate() method and invoke the createNotificationChannel() method:


override fun onCreate() {
super.onCreate()
createNotificationChannel()
}

Here is the full code

App.kt

import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
import com.mili.workmanagerandpendingnotification.Constants.CHANNEL_ID_ONE_TIME_WORK
import com.mili.workmanagerandpendingnotification.Constants.CHANNEL_ID_PERIOD_WORK

class App() : Application() {

override fun onCreate() {
super.onCreate()
createNotificationChannel()
}

private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_HIGH
val channelPeriodic = NotificationChannel(CHANNEL_ID_PERIOD_WORK, "Period Work Request", importance)
channelPeriodic.description = "Periodic Work"
val channelInstant = NotificationChannel(CHANNEL_ID_ONE_TIME_WORK, "One Time Work Request", importance)
channelInstant.description = "One Time Work"
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = applicationContext.getSystemService(
NotificationManager::class.java
)
notificationManager!!.createNotificationChannel(channelPeriodic)
notificationManager.createNotificationChannel(channelInstant)
}
}
}

Step 9: Create MainActivity

This is the laucher and only activity for this project. In this activity define a function that will observer changes for both One time work as well as the periodic work and report them to the UI:

    private fun observeChanges() {
viewModel.setPeriodWork().observe(this , Observer {
val periodWork = PeriodicWorkRequest.Builder(PeriodicBackgroundNotification::class.java,it.toLong(),TimeUnit.MINUTES)
.addTag("periodic-pending-notification")
.setConstraints(constraints)
.build()
SharedPrefHelpers.writeToSharedPreferences(this, LAST_PERIODIC_TIME, System.currentTimeMillis().toString())
WorkManager.getInstance(this).enqueueUniquePeriodicWork("periodic-pending-notification", ExistingPeriodicWorkPolicy.KEEP, periodWork)
})

viewModel.setOneTimeWork().observe(this, Observer {
val onetimeWork = OneTimeWorkRequest.Builder(OnetimeBackgroundNotification::class.java)
onetimeWork.setConstraints(constraints)
val data = Data.Builder()
data.putString(ONETIME_WORK_DESCRIPTION,it)
onetimeWork.setInputData(data.build())

WorkManager.getInstance(this).enqueue(onetimeWork.build())
})
}

Here is the full activity:

MainActivity.kt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.work.*
import com.mili.workmanagerandpendingnotification.Constants.LAST_PERIODIC_TIME
import com.mili.workmanagerandpendingnotification.Constants.ONETIME_WORK_DESCRIPTION
import com.mili.workmanagerandpendingnotification.databinding.ActivityMainBinding
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainViewModel
private val constraints = Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
binding.lifecycleOwner = this
binding.viewModel = viewModel

observeChanges()
}

private fun observeChanges() {
viewModel.setPeriodWork().observe(this , Observer {
val periodWork = PeriodicWorkRequest.Builder(PeriodicBackgroundNotification::class.java,it.toLong(),TimeUnit.MINUTES)
.addTag("periodic-pending-notification")
.setConstraints(constraints)
.build()
SharedPrefHelpers.writeToSharedPreferences(this, LAST_PERIODIC_TIME, System.currentTimeMillis().toString())
WorkManager.getInstance(this).enqueueUniquePeriodicWork("periodic-pending-notification", ExistingPeriodicWorkPolicy.KEEP, periodWork)
})

viewModel.setOneTimeWork().observe(this, Observer {
val onetimeWork = OneTimeWorkRequest.Builder(OnetimeBackgroundNotification::class.java)
onetimeWork.setConstraints(constraints)
val data = Data.Builder()
data.putString(ONETIME_WORK_DESCRIPTION,it)
onetimeWork.setInputData(data.build())

WorkManager.getInstance(this).enqueue(onetimeWork.build())
})
}
}

Run

Run the project.

Reference

Find the download link below:

No.Link
1.Download code
2.Browse code
3.Download code

Conclusion

WorkManager is a powerful library that simplifies the process of handling background tasks in Android. With its easy-to-use API, you can define and manage tasks with minimal effort, while still having the flexibility to specify constraints and observe their state and progress. If you're not already using WorkManager in your Android app, now is a great time to start.