WorkManager basics – One time request and chain

Reading Time: 13 minutes

In this WorkManager basics tutorial we are going to learn about how we can create one time request and chain. It means, that the work will be fired only once until it finishes successfully, then the next work will be started in thet case.

First, we gonna talk about how we can fire only one WorkRequest. Then we are going to create some more worker classes which will be chained after each other.

What is WorkManager?

WorkManager is part of the Android Jetpack library. It runs deferrable guaranteed backround work.

A deferrable task is one that doesn’t need to be executed immediately and may depend upon some constraints such as network conditions, storage space, and charging status.

A work can have different constraints, which determines the conditions when the work can be executed. A WorkManager’s work runs in the background even if the app is closed.

WorkManager has support for one time and periodic asnychronous execution as well.

Periodic execution means, that the next task well be fired when the previous task is successfully finished.

In the consciousness of these conditions, we can see thet WorkManager provides a battery-friendly API.

Battery-friendly, because it executes the tasks when it is the best to save the life of the battery avoiding unncessery wake-up for the phone. This is realy important for Android applications that need to execute background tasks!

When to use WorkManager?

When we need to do a long running task. This task can be started in the background, or also when the app is in the foreground, but needs to be done in the background, or during the execution the Android system kills its process.

WorkManager is not about run in a background thread. For this use cases you should use Kotlin Coroutines or libraries like RxJava.

The sample app

In this tutorial we are going to create a very simple application. Using the application we can see the status of the ordered pizza.

The ordering process gonna have 4 phases.

      1. Preparation
      2. Cooking
      3. Packing
      4. Delivery

These phases won’t do anything, just wait for few seconds to simulating the duration of the activity. When a phase is done, then the next one will be started automatically.

We are going to have 2 Activities. The first will contain only an “Order Pizza” button. The second Activity will show the current status of the order using a circular ProgressBar and a TextView. Under them we gonna have one more TextView, wich will show us where the phases were fulfilled.

Step 1 – Create new project

First thing first, we gonna create a whole new project. For this, launch Android Studio. If you see the “Welcome page”, then click on the “Start a new Android Studio project”. If you have an open project, then from the “File” menu select “New”, then “New Project”. After thet, this window will popup.

Here select the “Empty Activity” option. In the next window, we should provide some information about the newly created app.

This tutorial will contain very few code, but still we have to select the programming language. As the other tutorials on this website, this will be written also in Kotlin. So, select from the dropdown list the Kotlin language.

From the next list of the “Minimum SDK” select API 23. In our case API 23 gonna be enough.

If you are done, click on the “Finish” button. The build can take few minutes. Be patient! 😉

When the build has been finished, then you should see the open MainActivity::class and next to this the activity_main.xml files.

Step 2 – Add a dependency

In our sample pizza delivery application we are going to create worker classes. Inside of these classes we can create output data. These data can be requested from the Activity in form of an observable LiveData. Because of this we have to add the library for the lifecycle extension of the AndroidX as well.

We can do it as follows. Open up the App Build.gradle file from the left Project pane.

Next, paste the below implementations into the dependencies {} section.


The needed dependencies

JVM target

The WorkManager library targets the version 1.8 of Java Virtual Machine. So, we have to tell for Android Studio that we would like to use the version 1.8. To do this, stay in the App Build.gradle file and paste the below lines into the end of the android {} section.


JVM version 1.8

Thenafter click on the Sync now button, what you can find in Android Studio at the top left corner.

Step 3 – The colors and strings

After adding the needed dependencies, we are going to continue the creation by adding to the app some new colors and strings.

strings.xml

We will start first with the strings.xml file. You can find it in the res -> values folder.

Paste into the xml file the below lines, which are the strings of the for phases and a counter four the current state.


The strings

colors.xml

Thenafter add the below line to the colors.xml file, what you can find also in the res -> values folders.


The colors

Step 4 – The UI of MainActivity

For the MainActivity we will have a very simple user interface, because it will contain a rounded button with a nice blue background.

The shape of the button

For this, we will create a new shape, what will be an xml file. So, click with the right mouse button on the res -> drawable folder. Then from the popup window select the New thenafter the Drawable Resource File option. In the popup window name the new file as “shape_roundedcorners_blue”.

After thet, paste the below line into the shape xml file.


The shape of the button

The user interface

Finally open up the activity_main.xml file from the res -> layout folders and switch to Code or Split mode to see the xml source code of the file.

By clicking on this button the second Activity will be started. This second Activity will show us the current state of the ordered pizza.

Currently, there we have a TextView. So, replace it with the below Button.


The order button

Step 5 – Customize the ProgressBar

Before we gonna create the OrderingActivity, we have to create 2 new drawable xml files for the background of the ProgressBar and the drawable for its progress line.

shape_circle.xml

Just click on the drawable folder inside of the res folder with the right mouse button, select New and the Drawable resource file. Name it as “shape_circle.xml”. Theafter paste the below line into the xml file.

This shape is risponsible for the ring look for the ProgressBar.



shape_circle

circular_progress_bar.xml

Next xml file will be responsible for the shape and the color of the progressline. So, in the same way, create a new Drawable resource file in the drawable folder with the name of “circular_progress_bar.xml”.

Below you can see the xml code for the circular_progress_bar.xml file.



circular_progress_bar.xml

Step 6 – Create the OrderingActivity

When we click on the Order Pizza Button on the MainActivity, then the OrderingActivity will be opened. In this activity the ordering process gonna be started automatically, when the activity is opens. It means, thet we will fire the first WorkerRequest in the onCreate() method of the OrderingActivity.

To create a new Activity, click on the main source set with the right mouse button, select the New option from the popup menu, then move your mouse to the Activity, and from the submenu select the Empty Activity option. In the popup window name the new activity as “OrderingActivity”.

After the creation Android Studio will generate for us the OrderingActivity::class and the activity_ordering.xml file as well. Next, we need the activity_ordering.xml, because we gonna build up the user interface.

The UI of the OrderingActivity contains a Progressbar, which shows the current status of the order. Inside of it, we gonna show for the user in numeric form the current and the all phases. Below the Progressbar, we will notify the user when the phase has been finished.

Below you can see the xml code for the activity_ordering.xml file.



activity_ordering.xml

Step 7 – Utils

2 more steps before we can create our first Worker class. We need to define some constants and an Enum class for the states of the ordering process.

Later on we are going to send data from the Worker classes to the OrderingActivity using a LiveData. That’s why we need both, the constants and the enum as well.

First, create a new package called “utils” in the main source set.

Constants

Next, create a new Kotlin class in the utils folder, and name it as “Constants”. Thenafter paste into it the below String constants.



Constants.kt

DeliveryState Enum class

The second file will be the DeliveryState::enum also in the utils folder. So, create it as we have done in case of the Constants.kt file, but now be careful to create an Enum class.

If you would like to know more about Enum classes in Kotlin, then check out our tutorial.

Enum Class

The Enum class should look like below.



DeliveryState::enum

Step 8 – The workers

To let WorkManager do some tasks, we have to create a new class, which extends the Worker::class. When we did it, then we have to override the doWork() method. What is inside of this method, will be executed when we fire the class. The doWork() method runs synchronously on a background thread provided by WorkManager.

The doWork() method return a Result object, which informs the WorkManager service whether the work succeeded. In case of failure, the work should be retried. So, the Result object has 3 methods.

      1. Result.success() : The work finished successfully.
      2. Result.failure() : The work failed.
      3. Result.retry() : If the work failed, then the work should be retried in a later time.

So, as we have done in case of the utils package, we are going to create a new package for the worker classes as well. So, create it, and name it as “workers”. Inside of this package we are going to create the 4 worker classes.

Create the worker classes

Inside of the workers package create 4 Kotlin files with the below names:

      • CookingWorker
      • DeliveryWorker
      • PackingWorker
      • PreparationWorker

After the creation, extend the classes by Worker.

For example the header of the CookingWorker::class should look like below.



CookingWorker::class

Note thet the classes takes the Context and the WorkerParameters in the constructor, which are passed into the Worker::class.

Thenafter you have to implement in all classes the doWork() method. In our pizza delivery sample app this method will contain only a CoroutineScope, which has a delay method with few seconds to simulate the long running task inside of the worker.

Beside the delay, the Workers gonna send an output Data object, what we will catch later on using LiveData.

When a worker in the chain failed, then it can be retried in a later time.

To catch a failed WorkRequest you should wrap the code with a try-catch block. When the method failed, then it will jump to the catch part and there WorkManager gonna retry the worker.

In our case we won’t do anything in the worker classes what can be failed, so we won’t implement any try-catch blocks.

After this introduction, your Worker classes should look like below.

PreparationWorker



PreparationWorker::class

CookingWorker



CookingWorker::class

PackingWorker



PackingWorker::class

DeliveryWorker



DeliveryWorker::class

Note the workDataOf() method before the doWork() methods return. Using this method we can set a Data objects, what will be the output of the Worker classes.

Input and output is passed in and out via Data objects. Data objects are lightweight containers for key/value pairs. They are meant to store a small amount of data that might pass into and out from WorkRequests.

Source: Background Work with WorkManager – Codelab

Step 9 – The ViewModel

The next step is to create a ViewModel class. This class will contain the 4 LiveData and the order() method, which will be responsible to define the one time request and later on the chains for the delivery.

Create the DeliveryViewModel

As we have done it before, we are going to create a new package in the main source set for the ViewModel as well. So, create it with the name of “viewmodel”. When it is done, then create inside of this new package a new Kotlin file as well, and name it as “DeliveryViewModel”.

Thenafter extend the class with a constructor. The DeliveryViewModel will get the Application instance. Next, extend the class by AndroidViewModel, which will get the Application instance from the constructor.

Finally, the DeliveryViewModel should look like below.



DeliveryViewModel::class

WorkInfo

Next, we have to talk about the WorkInfo object. It is an object, which contains details about the current state of the running WorkRequest. It contains 

      • the status: BLOCKED, CANCELLED, ENQUEUED, FAILED, RUNNING or SUCCEEDED
      • When the Worker succeeded, then the output data from the work.

The 4 LiveData will contain these 4 WorkInfos of the 4 Workers. It means in our case, thet these LiveDatas will notify the OrderingActivity when the Worker reached a new status. When it has reached the SUCCEEDED state, then we can get the Data object, what we have passed inside of the doWork() methods.

Paste the below line into the DeliveryViewModel::class.



Create the LiveData objects

      1. Create an instance about the WorkManager::class using the instance of the Application from the constructor.
      2. The new LiveData instance variables for the WorkInfo‘s.
      3. The init block to initialize the LiveData variables using the getWorkInfosByTagLiveData() method. This method will request the output data from the worker classes.

Step 10 - Our first WorkRequest

First, we are going to fire only the PreparationWorker to see how we can create a WorkRequest only for one worker. Later on we gonna learn also how we can chain it with the next phases of the pizza delivery.

So, add the below method to the end of the DeliveryViewModel::class.



order()

      1. Using the OneTimeWorkRequestBuilder class we can create the WorkRequest. With the addTag() method we can add to the WorkRequest a tag. This is important, because later on we will use it to identify the output data from the Worker.
      2. Using the beginUniqueWork() method we can start the unique work of the prepartionBuilder.
        About the ExistingWorkPolicy.KEEP we gonna talk after this explanation.
      3. We should just enqueue to fire up the WorkRequest.

Using the ExistingWorkPolicy we can tell for the WorkManager what it should do with the still running WorkRequests. In our case we gonna use the KEEP option. It means, if we have an existing pending pizza order work, then during the order we can’t order one more pizza. Sorry 😊

If you would like to know more about the rest existing work policies, then check out the below link.

ExistingWorkPolicy

The Observer

Now we have defined the LiveDatas and a WorkRequest. The next step is to fire up this WorkRequest and observe its output data in the DeliveryActivity::class.

So, open up this activity. Then add the below member variable before the onCreate() method.

private lateinit var deliveryViewModel: DeliveryViewModel

Thenafter we gonna init the deliveryViewModel variable in the onCreate() method.



init the deliveryViewModel

Next is to setup the Observer for the outputWorkInfos_prep LiveData. So, paste the below line to the end of the onCreate() method.



The Observer

Now you should have an error, because we haven’t implemented the deliveryObserver() method. We solve it now, so paste the below method after the onCreate() method.



deliveryObserver()

Oh, one more red error?! 🤔

Yes, because we need one more method, which gonna update the pizza order’s status. So, paste the below method after the deliveryObserver() method.



setOrderStatus()

      1. Set the progress of the ProgressBar by calculating the percentage.
      2. Set the text of the tv_steps‘s TextView to show the current phase.
      3. Update the TextView with the order’s history.

Ok ok, I promise, this will be the last error in our application. 😀

The formatTime() method will be an extension function of the Date class. This function will give back a String with the time, when the phase of the pizza order had been finished.

Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use design patterns such as Decorator. This is done via special declarations called extensions.

Source: Kotlin Extensions

Paste the below extension function after the DeliveryActivity::class.



formatTime()

Step 11 - Start the OrderingActivity

Before we can run the app, we have to add a click listener for the Order Pizza Button in the MainActivity::class, otherwise our app won’t do anything. So, open the MainActivity::class from the main source set, and add the below method after the onCreate() method.



navigateToOrderActivity()

Then call this method from the click listener of the Order Pizza Button.



setOnClickListener

Run the app

Finally, it is time to run the app. 

When you click on the Order Pizza Button, then the app will navigate you to the OrderingActivity and the order process will be started automatically.

Currently only the first phase is fired, so let’s see how we can chain to it the rest phases.

Step 12 - Chain the workers

In this step we are going to chain the workers of the 4 phases of our pizza ordering system. So, basically, the phases have to be started when the previous phase is finished successfully.

Extend the DeliveryViewModel

Now, open the DeliveryViewModel::class from the viewmodel package and extend the order() method with the below lines. These lines should be placed before the orderingProcess.enqueue() line, but after the creation of the orderingProcess variable.



The phases of the ordering system

Note thet the OnTimeRequests are the same like what we have used for the preparation phase.

The more important thing is the then() method. Using this method we can attach a worker to the WorkManager. In this case it specifies the order of the workers, because they gonna be fired in in sequential order.

Extend the OrderingActivity

The next step is to add the Observers for the rest phases in the DeliveryActivity::class as well. So, open it from the main source set and add the below Observers to the onCreate() method.



The observers

Finally, extend the if-statement with the below else-if-statements in the deliveryObserver() method. These lines will catch the WorkInfo objects from the rest 3 Workers as well. The user interface will be notified based on these WorkInfos.



Catch the WorkInfos

Run the app

Run again the app and test it. It should work as you can see on the video below.

GitHub

The source code is available on GitHub, check it out and download it using the below link.

GitHub

Questions

I hope the description was understandable and clear. But, if you have still questions, then leave me comments below! 😉

Have a nice a day! 🙂

Follow and like us:

Click to rate this post!
[Total: 0 Average: 0]

Leave a comment

stay informed!

Subscribe to receive exclusive content and notifications