Android WorkManager example for periodic tasks

1. Added JAVA 1.8 compile options in the gradle file.

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
    jvmTarget = "1.8"
}

2. Add the workmanager dependency in the gradle file.

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

3. Create a worker class MyWorker.kt that does nothing but to sleep for 10 seconds and show a notification before and after it sleeps.

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

class MyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {

    override fun doWork(): Result {
        val appContext = applicationContext

        makeStatusNotification("Worker started work.", appContext)
        sleep(10)
        makeStatusNotification("Worker finshed work.", appContext)

        return Result.success()
    }

}

4. Run the worker. It can be run from anywhere in the app, in this case, we are running it when a button is clicked in MainActivity. It is set to run only when the phone is charging and battery is not low. It will be run once every 15 minute. It will run even if the activity is killed or the entire app is killed. To stop it, call WorkManager.getInstance().cancelAllWorkByTag("com.example.workmanagerexample.MyWorker"); The class path of the worker class is used as default TAG for the worker class if it is not set.

class MainActivity : AppCompatActivity() {

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

        start_worker.setOnClickListener {
            startWork()
        }
    }

    fun createConstraints() = Constraints.Builder()
            .setRequiresCharging(true)
            .setRequiresBatteryNotLow(true)
            .build()

    fun createWorkRequest(data: Data) = PeriodicWorkRequestBuilder<MyWorker>(15, TimeUnit.MINUTES)
            .setInputData(data)
            .setConstraints(createConstraints())
            .setBackoffCriteria(BackoffPolicy.LINEAR, PeriodicWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
            .build()

    fun startWork() {
        val work = createWorkRequest(Data.EMPTY)
        WorkManager.getInstance(applicationContext).enqueueUniquePeriodicWork("Sleep work", ExistingPeriodicWorkPolicy.REPLACE, work)
    }

}

The utility class for showing the notification WorkerUtil.kt

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import timber.log.Timber

/**
 * Create a Notification that is shown as a heads-up notification if possible.
 *
 * For this codelab, this is used to show a notification so that you know when different steps
 * of the background work chain are starting
 *
 * @param message Message shown on the notification
 * @param context Context needed to create Toast
 */
fun makeStatusNotification(message: String, context: Context) {

    // Make a channel if necessary
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        val name = VERBOSE_NOTIFICATION_CHANNEL_NAME
        val description = VERBOSE_NOTIFICATION_CHANNEL_DESCRIPTION
        val importance = NotificationManager.IMPORTANCE_HIGH
        val channel = NotificationChannel(CHANNEL_ID, name, importance)
        channel.description = description

        // Add the channel
        val notificationManager =
            context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?

        notificationManager?.createNotificationChannel(channel)
    }

    // Create the notification
    val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setContentTitle(NOTIFICATION_TITLE)
        .setContentText(message)
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setVibrate(LongArray(0))

    // Show the notification
    NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build())
}

/**
 * Method for sleeping for a fixed about of time to emulate slower work
 */
fun sleep(durationInSec: Long) {
    try {
        Thread.sleep(ONE_SECOND_IN_MILLIS * durationInSec, 0)
    } catch (e: InterruptedException) {
        Timber.e(e)
    }
}

The constants used in the above classes, Constants.kt

// Name of Notification Channel for verbose notifications of background work
@JvmField val VERBOSE_NOTIFICATION_CHANNEL_NAME: CharSequence =
    "Verbose WorkManager Notifications"
const val VERBOSE_NOTIFICATION_CHANNEL_DESCRIPTION =
    "Shows notifications whenever work starts"
@JvmField val NOTIFICATION_TITLE: CharSequence = "WorkRequest Starting"
const val CHANNEL_ID = "VERBOSE_NOTIFICATION"
const val NOTIFICATION_ID = 1
const val ONE_SECOND_IN_MILLIS: Long = 1000

References:
https://medium.com/androiddevelopers/workmanager-basics-beba51e94048

https://android.jlelse.eu/android-workmanager-manage-periodic-tasks-c13fa7744ebd

https://github.com/googlecodelabs/android-workmanager

Complete example in Gitbub

Search within Codexpedia

Custom Search

Search the entire web

Custom Search