Android JetPack - Scheduling Tasks with WorkManager
Introduction:
WorkManager is one of the Android JetPack component. It is specifically designed for scheduling and managing background tasks. If the app is running, task will be executed in a new thread. Otherwise, WorkManager automatically chooses an appropriate way to do so. It can be AlarmManager, JobScheduler or Firebase JobDispatcher, depends on device's API level and its dependencies.
WorkManager Basics:
- Worker
- WorkRequest
- WorkManager
- WorkInfo
(i) Worker:
It specifies what task need to be performed.
class SampleWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): ListenableWorker.Result {
//do the work here
return ListenableWorker.Result.SUCCESS
}
}
(ii) WorkRequest:
It represents which Worker should perform the task. Also we can specify any constraints/circumstances, based on which the task should be performed. Constraints can be of NetworkType, charging status, battery status, etc.
Two kinds of WorkRequest are there:
(a) OneTimeWorkRequest - Non-repeating work request.
val requestBuilder = OneTimeWorkRequest.Builder(SampleWorker::class.java).build()
(b) PeriodicWorkRequest - WorkRequest that repeats periodically.
val requestBuilder = PeriodicWorkRequest.Builder(SampleWorker::class.java, 24, TimeUnit.HOURS)
(iii) WorkManager:
It enqueues and manages the work requests.
WorkManager.getInstance().enqueue(requestBuilder)
We can also enqueue WorkRequest as unique, so that the same WorkRequest will not be assigned again and again.
val workTag = "workTag"
WorkManager.getInstance().beginUniqueWork(workTag, ExistingWorkPolicy.REPLACE, requestBuilder).enqueue()
(iv) WorkInfo:
It contains information about a particular task. We can get the WorkInfo of every WorkRequest, by observing LiveData.
WorkManager.getInstance().getStatusByIdLiveData(requestBuilder.id).observe(this@DataActivity, android.arch.lifecycle.Observer { workerStatus ->
if (workerStatus != null && workerStatus.state.isFinished) {
Toast.makeText(this@DataActivity, workerStatus.outputData.getString(Constants.EXTRA_OUTPUT_MESSAGE), Toast.LENGTH_SHORT).show()
}
})
You can find the Project on Github.
Implementation:
Let's first add WorkManager in our app-level gradle.
implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha10"
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.yamikrish.app.workmanagerdemo"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Temporary fix until alpha10
packagingOptions {
exclude 'META-INF/proguard/androidx-annotations.pro'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha10"
implementation "androidx.lifecycle:lifecycle-livedata:2.0.0"
kapt "androidx.lifecycle:lifecycle-compiler:2.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
Let's see about WorkManager in detail with example. So first, let's create an activity, where we can have no. of options to check each kind.
BaseActivity.kt
It have 4 buttons for showing SingleTask, PeriodicTask, ChainedTask and Constraints & Data Task.
BaseActivity.kt
It have 4 buttons for showing SingleTask, PeriodicTask, ChainedTask and Constraints & Data Task.
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import com.yamikrish.app.workmanagerdemo.R
import com.yamikrish.app.workmanagerdemo.activity.ChainTaskActivity
import com.yamikrish.app.workmanagerdemo.activity.ConstraintsDataActivity
import com.yamikrish.app.workmanagerdemo.activity.PeriodicTaskActivity
import com.yamikrish.app.workmanagerdemo.activity.SingleTaskActivity
import kotlinx.android.synthetic.main.activity_main.*
class BaseActivity : AppCompatActivity(), View.OnClickListener {
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_periodic_task -> startActivity(Intent(this@BaseActivity, PeriodicTaskActivity::class.java))
R.id.btn_single_task -> startActivity(Intent(this@BaseActivity, SingleTaskActivity::class.java))
R.id.btn_recurring_task -> startActivity(Intent(this@BaseActivity, ChainTaskActivity::class.java))
R.id.btn_constraints_data -> startActivity(Intent(this@BaseActivity, ConstraintsDataActivity::class.java))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_periodic_task.setOnClickListener(this)
btn_single_task.setOnClickListener(this)
btn_recurring_task.setOnClickListener(this)
btn_constraints_data.setOnClickListener(this)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
<Button
android:id="@+id/btn_single_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/single_task"
app:layout_constraintBottom_toTopOf="@+id/btn_periodic_task"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_periodic_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/periodic_task"
app:layout_constraintBottom_toTopOf="@+id/btn_recurring_task"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_single_task" />
<Button
android:id="@+id/btn_recurring_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chain_task"
app:layout_constraintBottom_toTopOf="@+id/btn_constraints_data"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_periodic_task" />
<Button
android:id="@+id/btn_constraints_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/constraints"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_recurring_task" />
</android.support.constraint.ConstraintLayout>
(i) Simple Work:
Lets begin with simple one ;) We can create a Worker, which will send a push-notification after starts. So, create an activity with one button to start the Worker. Once the button is clicked, we can enqueue the WorkRequest (sending pushnotification worker) to WorkManager.
We can also delay the start of the task with the help of setInitialDelay() method.
We can also delay the start of the task with the help of setInitialDelay() method.
setInitialDelay(10, TimeUnit.MINUTES)
SingleTaskActivity.kt
import android.os.Bundle
import android.support.annotation.Nullable
import android.support.v7.app.AppCompatActivity
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.yamikrish.app.workmanagerdemo.worker.SingleWorker
import com.yamikrish.app.workmanagerdemo.R
import kotlinx.android.synthetic.main.activity_start_task.*
class SingleTaskActivity : AppCompatActivity() {
private lateinit var workManager: WorkManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start_task)
supportActionBar?.apply {
setHomeButtonEnabled(true)
setDisplayHomeAsUpEnabled(true)
title = getString(R.string.single_task)
}
workManager = WorkManager.getInstance()
start_single_task.setOnClickListener {
val requestBuilder = OneTimeWorkRequest.Builder(SingleWorker::class.java).build()
workManager.enqueue(requestBuilder)
}
}
override fun onSupportNavigateUp(): Boolean {
finish()
return super.onSupportNavigateUp()
}
override fun onBackPressed() {
finish()
super.onBackPressed()
}
}
SingleWorker.kt
This Worker class is to create and send notifications in the app.
import android.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.support.v4.app.NotificationCompat
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
class SingleWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
override fun doWork(): ListenableWorker.Result {
sendNotification()
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_00"
//If on Oreo then notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle("Single Worker")
.setContentText("This notification is from Single Worker!!")
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(1, notification.build())
}
}
activity_start_task.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Button
android:id="@+id/start_single_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
(ii) Periodic Tasks:
PeriodicTaskActivity.kt
Next, we will see about PeriodicWorker. PeriodicWorker can schedule a task to be performed, for every specified time. It can be of MINUTES, HOURS or DAY. Time should be minimum of 15 minutes.
Let's create a Worker to send notification every 15 mins.
Let's create a Worker to send notification every 15 mins.
import android.content.Context
import android.content.SharedPreferences
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import androidx.work.*
import com.yamikrish.app.workmanagerdemo.worker.periodicWorker.NotifyWorker
import kotlinx.android.synthetic.main.activity_periodic_notifier.*
import java.util.concurrent.TimeUnit
import com.yamikrish.app.workmanagerdemo.R
import com.yamikrish.app.workmanagerdemo.worker.periodicWorker.NotifyInitialWorker
class PeriodicTaskActivity : AppCompatActivity() {
private lateinit var workManager: WorkManager
private val workTag = "starterNotifiWork"
private lateinit var prefs: SharedPreferences
private lateinit var editor: SharedPreferences.Editor
private val NOTIFICATION_STATUS = "notification_status"
private lateinit var requestBuilder: OneTimeWorkRequest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_periodic_notifier)
supportActionBar?.apply {
setHomeButtonEnabled(true)
setDisplayHomeAsUpEnabled(true)
title = getString(R.string.single_task)
}
prefs = getPreferences(Context.MODE_PRIVATE)
editor = prefs.edit()
workManager = WorkManager.getInstance()
remindSwitch.isChecked = prefs.getBoolean(NOTIFICATION_STATUS, false)
remindSwitch.setOnClickListener {
if (remindSwitch.isChecked) {
val requestBuilder = PeriodicWorkRequest.Builder(NotifyWorker::class.java, 15, TimeUnit.MINUTES)
workManager.enqueueUniquePeriodicWork(workTag, ExistingPeriodicWorkPolicy.REPLACE, requestBuilder.build())
} else {
workManager.cancelUniqueWork(workTag)
}
editor.putBoolean(NOTIFICATION_STATUS, remindSwitch.isChecked)
editor.apply()
}
override fun onSupportNavigateUp(): Boolean {
finish()
return super.onSupportNavigateUp()
}
override fun onBackPressed() {
finish()
super.onBackPressed()
}
}
NotifyWorker.kt
The above works fine at scheduling notification every 15 mins, even when the app is not in foreground or background state, also after device restart.
But one issue I encountered is, we can't set initial delay for PeriodicRequest, so that the task is sending notification while starting itself. It's not the correct way of doing it, know? (What to do??!!!)
Let's use some magic.. Nah..! Logic!!.
First we can create a OneTimeRequest with intialDelay of 15 minutes, then in that worker's doWork(), we can schedule the periodic tasks to show notification. Problem solved!!
PeriodicTaskActivity.kt
import android.util.Log
import androidx.work.Worker
import android.support.v4.app.NotificationCompat
import android.app.NotificationManager
import android.app.NotificationChannel
import android.content.Context
import android.content.Context.NOTIFICATION_SERVICE
import android.R;
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters
import android.support.annotation.NonNull
class NotifyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
override fun doWork(): ListenableWorker.Result {
sendNotification()
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_01"
//If on Oreo then notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle("Timer Reminder")
.setContentText("It's been 15 minutes!!!")
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(2, notification.build())
}
}
The above works fine at scheduling notification every 15 mins, even when the app is not in foreground or background state, also after device restart.
But one issue I encountered is, we can't set initial delay for PeriodicRequest, so that the task is sending notification while starting itself. It's not the correct way of doing it, know? (What to do??!!!)
Let's use some magic.. Nah..! Logic!!.
First we can create a OneTimeRequest with intialDelay of 15 minutes, then in that worker's doWork(), we can schedule the periodic tasks to show notification. Problem solved!!
PeriodicTaskActivity.kt
import android.content.Context
import android.content.SharedPreferences
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import androidx.work.*
import com.yamikrish.app.workmanagerdemo.worker.periodicWorker.NotifyWorker
import kotlinx.android.synthetic.main.activity_periodic_notifier.*
import java.util.concurrent.TimeUnit
import com.yamikrish.app.workmanagerdemo.R
import com.yamikrish.app.workmanagerdemo.worker.periodicWorker.NotifyInitialWorker
class PeriodicTaskActivity : AppCompatActivity() {
private lateinit var workManager: WorkManager
private val workTag = "starterNotifiWork"
private lateinit var prefs: SharedPreferences
private lateinit var editor: SharedPreferences.Editor
private val NOTIFICATION_STATUS = "notification_status"
private lateinit var requestBuilder: OneTimeWorkRequest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_periodic_notifier)
supportActionBar?.apply {
setHomeButtonEnabled(true)
setDisplayHomeAsUpEnabled(true)
title = getString(R.string.single_task)
}
prefs = getPreferences(Context.MODE_PRIVATE)
editor = prefs.edit()
workManager = WorkManager.getInstance()
remindSwitch.isChecked = prefs.getBoolean(NOTIFICATION_STATUS, false)
remindSwitch.setOnClickListener {
if (remindSwitch.isChecked) {
requestBuilder = OneTimeWorkRequest.Builder(NotifyInitialWorker::class.java)
.setInitialDelay(1, TimeUnit.MINUTES)
.build()
workManager.getStatusByIdLiveData(requestBuilder.id).observe(this@PeriodicTaskActivity, android.arch.lifecycle.Observer { workerStatus ->
if (workerStatus != null && workerStatus.state.isFinished) {
startNotifyWorker()
}
})
workManager.beginUniqueWork(workTag, ExistingWorkPolicy.REPLACE, requestBuilder).enqueue()
} else {
workManager.cancelAllWork()
}
editor.putBoolean(NOTIFICATION_STATUS, remindSwitch.isChecked)
editor.apply()
}
}
/** Schedule PeriodicWorkRequest
private fun startNotifyWorker() {
val requestBuilder = PeriodicWorkRequest.Builder(NotifyWorker::class.java, 15, TimeUnit.MINUTES)
WorkManager.getInstance().enqueueUniquePeriodicWork(workTag, ExistingPeriodicWorkPolicy.REPLACE, requestBuilder.build())
}
override fun onSupportNavigateUp(): Boolean {
finish()
return super.onSupportNavigateUp()
}
override fun onBackPressed() {
finish()
super.onBackPressed()
}
}
NotifyInitialWorker.kt
import android.content.Context
import androidx.work.*
class NotifyInitialWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): ListenableWorker.Result {
return ListenableWorker.Result.SUCCESS
}
}
activity_periodic_notifier.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".activity.PeriodicTaskActivity">
<TextView
android:id="@+id/infoText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remind me every 15 minutes"
android:padding="10dp"
android:textAppearance="@android:style/TextAppearance.Medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Switch
android:id="@+id/remindSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_margin="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
(iii) Chained Tasks
WorkManager can also schedule series of task to be executed in serial or parallel. Lets create three workers to send notifications.To enqueue it serially,
WorkManager.getInstance().beginWith(request1).then(request2).then(request3).enqueue()
WorkManager.getInstance().beginWith(request1).then(request2, request3).enqueue()
ChainTaskActivity.kt
import android.os.Bundle import android.support.v7.app.AppCompatActivity import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import com.yamikrish.app.workmanagerdemo.worker.chainWorker.FirstWorker import com.yamikrish.app.workmanagerdemo.worker.chainWorker.SecondWorker import com.yamikrish.app.workmanagerdemo.worker.chainWorker.ThirdWorker import kotlinx.android.synthetic.main.activity_start_task.* import com.yamikrish.app.workmanagerdemo.R import java.util.concurrent.TimeUnit class ChainTaskActivity : AppCompatActivity() { private lateinit var workManager: WorkManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_start_task) supportActionBar?.apply { setHomeButtonEnabled(true) setDisplayHomeAsUpEnabled(true) title = getString(R.string.single_task) } workManager = WorkManager.getInstance() start_single_task.setOnClickListener { val request1 = OneTimeWorkRequest.Builder(FirstWorker::class.java).build() val request2 = OneTimeWorkRequest.Builder(SecondWorker::class.java).setInitialDelay(30, TimeUnit.SECONDS).build() val request3 = OneTimeWorkRequest.Builder(ThirdWorker::class.java).setInitialDelay(30, TimeUnit.SECONDS).build() workManager.beginWith(request1).then(request2).then(request3).enqueue() } } override fun onSupportNavigateUp(): Boolean { finish() return super.onSupportNavigateUp() } override fun onBackPressed() { finish() super.onBackPressed() } }
We can also make requests run parrallelly, by queuing like below:
workManager.beginWith(request1).then(request2, request3).enqueue()
FirstWorker.kt
import android.R import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.support.v4.app.NotificationCompat import androidx.work.ListenableWorker import androidx.work.Worker import androidx.work.WorkerParameters class FirstWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { val mContext = context override fun doWork(): ListenableWorker.Result { sendNotification() return ListenableWorker.Result.SUCCESS } fun sendNotification() { val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "WorkManager_02" //If on Oreo then notification required a notification channel. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(mContext, channelId) .setContentTitle("WorkManager") .setContentText("Message from First Worker!!") .setSmallIcon(R.drawable.ic_lock_idle_alarm) notificationManager.notify(3, notification.build()) } }
SecondWorker.kt
import android.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.support.v4.app.NotificationCompat
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
class SecondWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
override fun doWork(): ListenableWorker.Result {
sendNotification()
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_03"
//If on Oreo then notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle("WorkManager")
.setContentText("Message from Second Worker!!")
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(4, notification.build())
}
}
ThirdWorker.kt
import android.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.support.v4.app.NotificationCompat
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
class ThirdWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
override fun doWork(): ListenableWorker.Result {
sendNotification()
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_04"
//If on Oreo then notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle("WorkManager")
.setContentText("Message from Third Worker!!")
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(5, notification.build())
}
}
(iv) Constraints & Data
Now, let's see how to set constraints and how to send and receive data to/from Worker. We can easily set constraints to the Worker, by using,val constraints = Constraints.Builder().setRequiresCharging(true).build()
We can send data to Worker and also can receive data from Worker.
To send data:
val data = Data.Builder()
.putString(Constants.EXTRA_TITLE, "Title")
.putString(Constants.EXTRA_MESSAGE, "Message sent!!")
.build()
val requestBuilder = OneTimeWorkRequest.Builder(MyWorker::class.java) .setInputData(data) .build()
To receive data:
title = inputData.getString(Constants.EXTRA_TITLE)
message = inputData.getString(Constants.EXTRA_MESSAGE)
We can send OutputData from Worker to Activity/Fragment as well. We have to set OutputData before success return, ListenableWorker.Result.SUCCESS.
val output = Data.Builder()
.putString(EXTRA_OUTPUT_MESSAGE, "Message from Worker!")
.build()
outputData = output
ConstraintDataActivity.kt
In this class, let's send Data from Activity to Worker and send notification with that data. Once done, send OutputData back to the called Activity. To receive that output data, that Activity can listen by using LiveData.
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import androidx.work.Constraints
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.yamikrish.app.workmanagerdemo.utils.Constants
import com.yamikrish.app.workmanagerdemo.worker.constraintDataWorker.ConstraintWorker
import kotlinx.android.synthetic.main.activity_start_task.*
import com.yamikrish.app.workmanagerdemo.R
class ConstraintsDataActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start_task)
supportActionBar?.apply {
setHomeButtonEnabled(true)
setDisplayHomeAsUpEnabled(true)
title = getString(R.string.single_task)
}
val constraints = Constraints.Builder().setRequiresCharging(true).build()
val data = Data.Builder()
.putString(Constants.EXTRA_TITLE, "Message!!")
.putString(Constants.EXTRA_MESSAGE, "Message sent from Activity")
.build()
start_single_task.setOnClickListener {
val requestBuilder = OneTimeWorkRequest.Builder(ConstraintWorker::class.java)
.setConstraints(constraints)
.setInputData(data)
.build()
WorkManager.getInstance().enqueue(requestBuilder)
WorkManager.getInstance().getStatusByIdLiveData(requestBuilder.id).observe(this@ConstraintsDataActivity, android.arch.lifecycle.Observer { workerStatus ->
if (workerStatus != null && workerStatus.state.isFinished) {
Toast.makeText(this@ConstraintsDataActivity, workerStatus.outputData.getString(Constants.EXTRA_OUTPUT_MESSAGE), Toast.LENGTH_SHORT).show()
}
})
}
}
override fun onSupportNavigateUp(): Boolean {
finish()
return super.onSupportNavigateUp()
}
override fun onBackPressed() {
finish()
super.onBackPressed()
}
}
ConstraintWorker.kt
import android.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.support.v4.app.NotificationCompat
import androidx.work.Data
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.yamikrish.app.workmanagerdemo.utils.Constants
import com.yamikrish.app.workmanagerdemo.utils.Constants.Companion.EXTRA_OUTPUT_MESSAGE
class ConstraintWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
var title: String? = ""
var message: String? = ""
override fun doWork(): ListenableWorker.Result {
title = inputData.getString(Constants.EXTRA_TITLE)
message = inputData.getString(Constants.EXTRA_MESSAGE)
sendNotification()
val output = Data.Builder()
.putString(EXTRA_OUTPUT_MESSAGE, "Message from ConstraintWorker!")
.build()
outputData = output
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_05"
//If on Oreo then notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(6, notification.build())
}
}
DataWorker.kt
import android.R
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.support.v4.app.NotificationCompat
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
class DataWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
val mContext = context
override fun doWork(): ListenableWorker.Result {
sendNotification()
return ListenableWorker.Result.SUCCESS
}
fun sendNotification() {
val notificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "WorkManager_06"
//In Oreo, notification required a notification channel.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, "WorkManager", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setContentTitle("WorkManager")
.setContentText("Message from Data Worker!!")
.setSmallIcon(R.drawable.ic_lock_idle_alarm)
notificationManager.notify(7, notification.build())
}
}
Constants.kt
Comments
Post a Comment