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:
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.
When clicked Send Notification with getRequiredNetworkType() Button
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 :
|
|
|
1319 Views
|