Android WorkManager Jetpack for Background Work

 
This post shows you how andorid jetpack work manager executes background tasks. Android WorkManager is intended for tasks required to run reliably even if the app exits or the device restarts.  You can check out sample app at HERE. 
 

Android WorkManager is not intended for an in-process background task that can be safely terminated if the app process runs or for tasks requiring immediate execution.  Android WorkManager provides great feature Task Chaining. We can define multiple tasks if we want. New task can start only after old task is finished.

Job Scheduler is available only in android lollipop or after versions. But Android WorkManager is compatible with all android versions. So We do not need to check android version before use of android workmanager.

Minimum duration of android workmanager is 15 minutes. So your work execution can repeat only after a minimum 15 minutes. If you want to repeat your work after less than 15 minutes then you need to create foreground service.

 

Add the below dependency in your app level build.gradle file

dependencies {
    implementation 'androidx.work:work-runtime-ktx:2.3.4'
}



Create a layout file for activity

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_one_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start One Time Request"
        android:textAllCaps="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.4" />

    <Button
        android:id="@+id/btn_periodic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Periodic Request"
        android:textAllCaps="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.6" />

</androidx.constraintlayout.widget.ConstraintLayout>
 
 

Add Work Constraints if needed

This constraints indicate that when work request can run. There are many constraints which are used in android workmanager. setRequiresCharging is used to execute work only when device is charging. If you want to execute work only when battery is not low then setRequiresBatteryNotLow is used. setRequiresDeviceIdle is used to execute work when device is idle.
 
val constraints = Constraints.Builder()
                          .setRequiresDeviceIdle(true)
                          .setRequiresCharging(true)
                          .build()
val oneTimeReq = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setConstraints(constraints)
            .build()
 
 

Add Initial Delays if needed

val  oneTimeReq = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setInitialDelay(2,TimeUnit.MINUTES)
            .build()
 

 

Add Input Parameters if needed

If your task requires data than you can also pass data as input parameters. For example if you want to show notification periodically after some interval than you can pass notification data to your worker class as below. 
 
val data = Data.Builder()
            .putString(“channel_id”,”periodic_work”)
            .putString(“title”, “Periodic Request”)
            .putString(“message”, “This notification is from Periodic Request”)
            .build()
val periodicReq = PeriodicWorkRequest.Builder(MyWorker::class.java,15,TimeUnit.MINUTES)
            .setInputData(data)
            .build()
 
 

Get Data in Worker class

class MyWorker(private val context: Context, private val workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {
        val getData = workerParams.inputData
        val channelId = getData.getString(“channel_id”)
        val title = getData.getString(“title”)
        val message = getData.getString(“message”)
        context.sendNotification(channelId,title,message)
        return Result.success()
    }
}
 
 

Execute WorkRequests

There are two types of work requests in work manager: OneTimeWorkRequest and PeriodicWorkRequest. 
 
 

For OneTimeWorkRequest:

 OneTimeWorkRequest is used to perform work only once.

 val oneTimeReq = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setConstraints(constraints)
            .setInputData(data)
            .build()
val workManager = WorkManager.getInstance(this)
workManager.enqueue(oneTimeReq)
 
 

For PeriodicWorkRequest:

PeriodicWorkRequest is used when work is needed to perform periodically.
 
val periodicReq = PeriodicWorkRequest.Builder(MyWorker::class.java,15,TimeUnit.MINUTES)
            .setConstraints(constraints)
            .setInputData(data)
            .build()
val workManager = WorkManager.getInstance(this)      workManager.enqueueUniquePeriodicWork(“start_periodic_work”,ExistingPeriodicWorkPolicy.REPLACE,periodicReq)
 
 

Cancelling WorkRequests

We can also cancel the work if we want. For this we need id of workrequest and pass it to cancelworkById method of workmanager.
 

For OneTimeWorkRequest:

 
workManager.cancelWorkById(oneTimeReq.id)
 
 

For PeriodicWorkRequest:

 
workManager.cancelWorkById(periodicReq.id)
 
 

Final Code

package com.example.workmanagersample

import android.app.*
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.Worker
import androidx.work.WorkerParameters

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

    override fun doWork(): Result {
        val getData = workerParams.inputData
        val channelId = getData.getString("channel_id")
        val title = getData.getString("title")
        val message = getData.getString("message")
        context.sendNotification(channelId,title,message)
        return Result.success()
    }

    private fun Context.sendNotification(
        channelId: String?,
        contentTitle: String?,
        contentText: String?
    ) {
        try {
            // Get an instance of the Notification manager
            val mNotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

            // Android O requires a Notification Channel.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // Create the channel for the notification
                val mChannel =
                    NotificationChannel(channelId, getString(R.string.app_name) , NotificationManager.IMPORTANCE_HIGH)

                // Set the Notification Channel for the Notification Manager.
                mNotificationManager.createNotificationChannel(mChannel)
            }

            // Get a notification builder that's compatible with platform versions >= 4
            val builder = NotificationCompat.Builder(this)

            // Define the notification settings.
            builder.setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle(contentTitle)
                .setContentText(contentText)


            // Set the Channel ID for Android O.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                channelId?.let { builder.setChannelId(it) } // Channel ID
            } else {
                builder.priority = Notification.PRIORITY_HIGH
            }

            // Dismiss notification once the user touches it.
            builder.setAutoCancel(true)

            // Issue the notification
            mNotificationManager.notify(0, builder.build())
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

}

package com.example.workmanagersample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.work.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    private lateinit var workManager: WorkManager
    private lateinit var oneTimeReq: OneTimeWorkRequest
    private lateinit var periodicReq: PeriodicWorkRequest

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

        workManager = WorkManager.getInstance(this)

        btn_one_time.tag = "stopped"
        btn_periodic.tag = "stopped"

        btn_one_time.setOnClickListener {
            if(it.tag == "stopped"){
                btn_one_time.tag = "started"
                btn_one_time.text = "Cancel One Time Request"
                startOneTimeRequest()
            } else{
                btn_one_time.tag = "stopped"
                btn_one_time.text = "Start One Time Request"
                workManager.cancelWorkById(oneTimeReq.id)
            }
        }

        btn_periodic.setOnClickListener {
            if(it.tag == "stopped"){
                btn_periodic.tag = "started"
                btn_periodic.text = "Cancel Periodic Request"
                startPeriodicRequest()
            } else{
                btn_periodic.tag = "stopped"
                btn_periodic.text = "Start Periodic Request"
                workManager.cancelWorkById(periodicReq.id)
            }
        }

    }

    private fun startOneTimeRequest(){
        val constraints = Constraints.Builder()
            .build()

        val data = Data.Builder()
            .putString("channel_id","one_time_work")
            .putString("title", "One Time Request")
            .putString("message", "This notification is from One Time Request")
            .build()

        oneTimeReq = OneTimeWorkRequest.Builder(MyWorker::class.java)
            .setConstraints(constraints)
            .setInputData(data)
            .build()

        workManager.enqueue(oneTimeReq)
    }

    private fun startPeriodicRequest(){
        val constraints = Constraints.Builder()
            .build()

        val data = Data.Builder()
            .putString("channel_id","periodic_work")
            .putString("title", "Periodic Request")
            .putString("message", "This notification is from Periodic Request")
            .build()

        periodicReq = PeriodicWorkRequest.Builder(MyWorker::class.java,15,TimeUnit.MINUTES)
            .setConstraints(constraints)
            .setInputData(data)
            .build()

        workManager.enqueueUniquePeriodicWork("start_periodic_work",ExistingPeriodicWorkPolicy.REPLACE,periodicReq)
    }

}

 

android workmanager



3 thoughts on “Android WorkManager Jetpack for Background Work

Leave a Reply