How to schedule tasks with WorkManager in Android Studio using Kotlin.

Last updated Dec 23, 2021


In this android example tutorial, we will learn what is WorkManager and how to schedule tasks with WorkManager in android studio using kotlin language.

What is WorkManager?
WorkManager is a background processing library that is used to conduct background activities that must be completed in a certain amount of time but not necessarily immediately.

Using WorkManager We can enqueue our background processing when the app is not running and the device is reset for whatever reason. WorkManager also allows us to specify the
conditions that must be met in order for the task to be completed, such as network availability before beginning the background task.

WorkManager is one of the Android Architecture Components and is part of the Android Jetpack (a collection of libraries designed to assist developers create high-quality apps,
robust, testable,and easily maintainable apps). 
 

Features of WorkManager:

  •   It is totally backward compatible, you do not need to include any if-else statements in your code to check the Android version.
  •  We can check the status of the work using WorkManager.
  •  Tasks can also be chained, so that when one is completed, it can start another.
  •  And it guarantees execution with the restrictions; we have numerous constraints accessible that we will see in the future.

 

WorkManager Implementation Demo Application to Schedule Tasks:

Step 1. Create a new Project in android studio.

Go to File > New > New Project > Google Maps Activity > Next > Enter Name > Select Language Kotlin > Finish.

After creating the new project, Android Studio starts Gradle and builds your project, which may take a few seconds.
 

Step2.  Add WorkManager dependency in app/buid.gradle file
For using WorkManager we have to add dependency in app/build.gradle file. So let’s open the app build.gradle file and add below lines.

implementation "android.arch.work:work-runtime:1.0.1"

 

Step3. Open activity_main.xml file and add the following code

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Result display here"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="20dp"
        android:text="Send Notification without Constraints"
        android:textAllCaps="false"
        android:textColor="#fff" />

    <Button
        android:id="@+id/buttonStorageNotLow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="10dp"
        android:text="Send Notification with requiresStorageNotLow()"
        android:textAllCaps="false"
        android:textColor="#fff"
       />

    <Button
        android:id="@+id/buttonBatteryNotLow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="10dp"
        android:text="Send Notification with requiresBatteryNotLow()"
        android:textAllCaps="false"
        android:textColor="#fff" />

    <Button
        android:id="@+id/buttonNetworkType"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="@color/purple_500"
        android:text="Send Notification with getRequiredNetworkType()"
        android:textAllCaps="false"
        android:textColor="#fff" />
</LinearLayout>

 

This layout will contain TextView and Buttons. After that, we will set onClickListener() and this event will enqueue the WorkRequest to WorkManager and shows the status on TextView.

The above xml code will give you following output:

Android WorkManager Output

 

Step 4.  We create a base class of Worker class and override un-implemented methods and super constructor.
For create a new Kotlin Class

app > java > com.example.workmanagerproject > right-click > new > Kotlin Class/File > Enter Name (NotificationWorker) > Enter.

 

 Add the following code to NotificationWorker.kt file

 class NotificationWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {
        val taskData = inputData
        val taskDataString = taskData.getString(MainActivity.MESSAGE_STATUS)
        showNotification("WorkManager", "Message has been Sent")
        val outputData = Data.Builder().putString(WORK_RESULT, "Jobs Finished").build()
        return Result.success(outputData)
    }

    private fun showNotification(task: String, desc: String) {
        val manager =
            applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channelId = "task_channel"
        val channelName = "task_name"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel =
                NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
            manager.createNotificationChannel(channel)
        }
        val builder = NotificationCompat.Builder(applicationContext, channelId)
            .setContentTitle(task)
            .setContentText(desc)
            .setSmallIcon(R.drawable.ic_launcher_background)
        manager.notify(1, builder.build())
    }

    companion object {
        private const val WORK_RESULT = "work_result"
    }
}


 

Step 5. Create WorkRequest

Let's move to MainActivity and initialize the views below setContentView(R.layout.activity_main).

        tvStatus = findViewById(R.id.tvStatus)
        btnSend = findViewById(R.id.btnSend)
        btnStorageNotLow = findViewById(R.id.buttonStorageNotLow)
        btnBatteryNotLow = findViewById(R.id.buttonBatteryNotLow)
        btnNetworkType = findViewById(R.id.buttonNetworkType)
        mWorkManager = WorkManager.getInstance()

 

Make a WorkRequest to execute the work we just created. First, we'll make WorkManager. This work manager will manage and enqueue our work request.

mWorkManager = WorkManager.getInstance()

 

Now we will create OneTimeWorkRequest, because we want to create a task that will be executed just once.

mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java).build()


Using this code, we built work request that will be executed one time only.

 

Enqueue the request with WorkManager

In this step, we discuss onClick() of the button. we will enqueue this request using the WorkManager. So that’s all we need to do.

 

mWorkManager!!.enqueue(mRequest!!)

 

Fetch the particular task status

We'll get some information about this specific task and present it in the tvStatus TextView. We will accomplish this by use the WorkInfo class. The work manager exposes LiveData for each of the work request objects, which we can inspect to determine the task's current state.

mWorkManager!!.getWorkInfoByIdLiveData(mRequest!!.id).observe(this, { workInfo ->
            if (workInfo != null) {
                val state = workInfo.state
                tvStatus!!.append(
                    """
    $state
    
    """.trimIndent()
                )
            }
        })

 

The MainActivity.kt file will be look after added above code:

class MainActivity : AppCompatActivity(), View.OnClickListener {
    var tvStatus: TextView? = null
    var btnSend: Button? = null
    var btnStorageNotLow: Button? = null
    var btnBatteryNotLow: Button? = null
    var btnNetworkType: Button? = null
    var mRequest: OneTimeWorkRequest? = null
    var mWorkManager: WorkManager? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initViews()
      
    }

    private fun initViews() {
        tvStatus = findViewById(R.id.tvStatus)
        btnSend = findViewById(R.id.btnSend)
        btnStorageNotLow = findViewById(R.id.buttonStorageNotLow)
        btnBatteryNotLow = findViewById(R.id.buttonBatteryNotLow)
        btnNetworkType = findViewById(R.id.buttonNetworkType)
        mWorkManager = WorkManager.getInstance()
        btnSend!!.setOnClickListener(this)
        btnStorageNotLow!!.setOnClickListener(this)
        btnBatteryNotLow!!.setOnClickListener(this)
        btnNetworkType!!.setOnClickListener(this)
    }

    override fun onClick(v: View) {
        tvStatus!!.text = ""
        val mConstraints: Constraints
        when (v.id) {
            R.id.btnSend ->
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java).build()
            R.id.buttonStorageNotLow -> {
                /**
                 * Constraints
                 * If TRUE task execute only when storage's is not low
                 */
                mConstraints = Constraints.Builder().setRequiresStorageNotLow(true).build()
                /**
                 * OneTimeWorkRequest with requiresStorageNotLow Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            R.id.buttonBatteryNotLow -> {
                /**
                 * Constraints
                 * If TRUE task execute only when battery isn't low
                 */
                mConstraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
                /**
                 * OneTimeWorkRequest with requiresBatteryNotLow Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            R.id.buttonNetworkType -> {
                /**
                 * Constraints
                 * Network type is conneted
                 */
                mConstraints =
                    Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
                /**
                 * OneTimeWorkRequest with requiredNetworkType Connected Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            else -> {
            }
        }
        /**
         * Fetch the particular task status using request ID
         */
        mWorkManager!!.getWorkInfoByIdLiveData(mRequest!!.id).observe(this, { workInfo ->
            if (workInfo != null) {
                val state = workInfo.state
                tvStatus!!.append(
                    """
    $state
    
    """.trimIndent()
                )
            }
        })
        /**
         * Enqueue the WorkRequest
         */
        mWorkManager!!.enqueue(mRequest!!)
    }

    companion object {
        const val MESSAGE_STATUS = "message_status"
    }
}


Step 6. Now run the app in your emulator or real device, you will get the following output:

OUTPUT:

When clicked Send Notification without Constraints Button.

Android WorkManager Shcedule task

 

Android WorkManager Shcedule task 1


When clicked Send Notification with getRequiredNetworkType() Button

Android WorkManager Shcedule task 2

 

Complete Source Code of WorkManager with Example:

activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Result display here"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="20dp"
        android:text="Send Notification without Constraints"
        android:textAllCaps="false"
        android:textColor="#fff" />

    <Button
        android:id="@+id/buttonStorageNotLow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="10dp"
        android:text="Send Notification with requiresStorageNotLow()"
        android:textAllCaps="false"
        android:textColor="#fff"
       />

    <Button
        android:id="@+id/buttonBatteryNotLow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/purple_500"
        android:layout_marginTop="10dp"
        android:text="Send Notification with requiresBatteryNotLow()"
        android:textAllCaps="false"
        android:textColor="#fff" />

    <Button
        android:id="@+id/buttonNetworkType"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="@color/purple_500"
        android:text="Send Notification with getRequiredNetworkType()"
        android:textAllCaps="false"
        android:textColor="#fff" />
</LinearLayout>

 

MainActivity.kt file

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.work.WorkManager
import android.view.View
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest


class MainActivity : AppCompatActivity(), View.OnClickListener {
    var tvStatus: TextView? = null
    var btnSend: Button? = null
    var btnStorageNotLow: Button? = null
    var btnBatteryNotLow: Button? = null
    var btnNetworkType: Button? = null
    var mRequest: OneTimeWorkRequest? = null
    var mWorkManager: WorkManager? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initViews()
    }

    private fun initViews() {
        tvStatus = findViewById(R.id.tvStatus)
        btnSend = findViewById(R.id.btnSend)
        btnStorageNotLow = findViewById(R.id.buttonStorageNotLow)
        btnBatteryNotLow = findViewById(R.id.buttonBatteryNotLow)
        btnNetworkType = findViewById(R.id.buttonNetworkType)
        mWorkManager = WorkManager.getInstance()
        btnSend!!.setOnClickListener(this)
        btnStorageNotLow!!.setOnClickListener(this)
        btnBatteryNotLow!!.setOnClickListener(this)
        btnNetworkType!!.setOnClickListener(this)
    }

    override fun onClick(v: View) {
        tvStatus!!.text = ""
        val mConstraints: Constraints
        when (v.id) {
            R.id.btnSend ->
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java).build()
            R.id.buttonStorageNotLow -> {
                /**
                 * Constraints
                 * If TRUE task execute only when storage's is not low
                 */
                mConstraints = Constraints.Builder().setRequiresStorageNotLow(true).build()
                /**
                 * OneTimeWorkRequest with requiresStorageNotLow Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            R.id.buttonBatteryNotLow -> {
                /**
                 * Constraints
                 * If TRUE task execute only when battery isn't low
                 */
                mConstraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
                /**
                 * OneTimeWorkRequest with requiresBatteryNotLow Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            R.id.buttonNetworkType -> {
                /**
                 * Constraints
                 * Network type is conneted
                 */
                mConstraints =
                    Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
                /**
                 * OneTimeWorkRequest with requiredNetworkType Connected Constraints
                 */
                mRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
                    .setConstraints(mConstraints).build()
            }
            else -> {
            }
        }
        /**
         * Fetch the particular task status using request ID
         */
        mWorkManager!!.getWorkInfoByIdLiveData(mRequest!!.id).observe(this, { workInfo ->
            if (workInfo != null) {
                val state = workInfo.state
                tvStatus!!.append(
                    """
    $state
    
    """.trimIndent()
                )
            }
        })
        /**
         * Enqueue the WorkRequest
         */
        mWorkManager!!.enqueue(mRequest!!)
    }

    companion object {
        const val MESSAGE_STATUS = "message_status"
    }
}

 

NotificationWorker.kt file

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters

class NotificationWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {
        val taskData = inputData
        val taskDataString = taskData.getString(MainActivity.MESSAGE_STATUS)
        showNotification("WorkManager", "Message has been Sent")
        val outputData = Data.Builder().putString(WORK_RESULT, "Jobs Finished").build()
        return Result.success(outputData)
    }

    private fun showNotification(task: String, desc: String) {
        val manager =
            applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channelId = "task_channel"
        val channelName = "task_name"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel =
                NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
            manager.createNotificationChannel(channel)
        }
        val builder = NotificationCompat.Builder(applicationContext, channelId)
            .setContentTitle(task)
            .setContentText(desc)
            .setSmallIcon(R.drawable.ic_launcher_background)
        manager.notify(1, builder.build())
    }

    companion object {
        private const val WORK_RESULT = "work_result"
    }
}

 

build.gradle(app) file

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.example.workmanagerproject"
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    implementation 'androidx.test:core-ktx:1.4.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation "android.arch.work:work-runtime:1.0.1"

}

 

Conclusion: In this article we have covered how to schedule tasks with WorkManager in Android Studio using Kotlin language.


Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

23 Views

Subscribe For Daily Updates

Flutter Questions
Android Questions