Android Jetpack

Android WorkManager Tutorial

Pinterest LinkedIn Tumblr

Welcome back our Android Architecture Components tutorials series. In this article, we will learn what is WorkManager API in Android ? how it works and what are its advantages. So let’s get started.

Why WorkManager

Since Marshmallow, The Android dev team is continuously working on battery optimizations. After that team introduced Doze mode. Then in Oreo imposed various kind of limitation on performing background jobs. Before WorkManager, we use various job scheduler for performing background task, such as Firebase JobDispatcher, Job Scheduler and Alarm Manager + Broadcast receivers. So for the developer perspective, it is difficult to choose which scheduler should use and which one is good. So the Work Manager handles these kinds of stuff. We have to pass the task to the WorkManager and It uses all this Firebase Job Dispatcher, Alarm Manager + Broadcast Receivers, Job Scheduler to perform the background task depending on the requirement.

What is WorkManager?

WorkManager is basically a task scheduler, It makes it easy to specify the asynchronous task and when they should run. The Work Manager API helps create the task and hand it to the Work Manager to run immediately or at an appropriate time as mentioned. For example, you might point your app to download new resources from the network from time to time and now the downloading is a task and you can set up this task to run at an appropriate time depending on the availability of the WIFI network or when the device is charging. So this way you can schedule a task using WorkManager.

Advantages of WorkManager

  • It provides guaranteed and constraint-aware execution. In other words, You can schedule a task depending on the condition when they should run. So WorkManager will take care of all the constraints and give the guarantee that the task will be executed even when the device is rebooted are the app is exited without executing the task and our task will be executed.
  • It provides backward compatibility which means you don’t need to figure out the device capabilities or choose an appropriate API. You have to just hand off the task to the WorkManager. It will choose the best option for the execution of the task.
  • WorkManager supports tasks query, You can not only schedule the task but if you have quite some work or the tasks, you can actually check the status of the state of the task, whether it is running or whether it is quit or it has succeeded or failed. so you can detect the status of tasks.
  • It supports task chaining, this means you can create a draft of your work and enqueue one after the other using the Work Manager.

How it works

Before moving forward, We have to understand the class and concept of WorkManager. Let’s understand what are various base classes that are used for Job Scheduling.

– Worker

It specifies what task to perform, The WorkManager API include an abstract worker class and You need to extends this class and perform the work.

– WorkRequest

WorkRequest represents an individual task that is to be performed. Now this WorkRequest, you can add values details for the work. Such as constraint or you can also add data while creating the request

WorkRequest can be of to type
  • OneTimeWorkRequest– That means you requesting for non-repetitive work.
  • PeriodicWorkRequest– This class is used for creating a request for repetitive work

– WorkManager

The WorkManager class in enqueues and manages all the work request. We pass work request object to this WorkManager to enqueue the task.

– WorkInfo

WorkInfo contains the information about a particular task, The work manager provides LiveData for each of the work request objects, We can observe this and get the current status of the task.

Step for implementation WorkManager to Schedule Tasks

  1. Create a new project and add WorkManager dependency in app/buid.gradle file
  2. Create a base class of Worker
  3. Create WorkRequest
  4. Enqueue the request with WorkManager.
  5. Fetch the particular task status

Lets we will create demo application using the above steps.

1. Add dependency

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.0"

In this demo app, we will create a layout. This layout will contain TextView and Button. After that, we will set onClickListener() and this event will enqueue the WorkRequest to WorkManager and shows the status on TextView. Let open the AndroidStudio and create a new project with a basic activity template.

Add Button and TextView in activity_main.xml layout files.
<?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=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:gravity="center"
        android:text="Hello World!"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:background="@color/colorAccent"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:text="Send Notification"
        android:textAllCaps="false"
        android:textColor="#fff"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</android.support.constraint.ConstraintLayout>

2. Create a base class of Worker

Create a base class of Worker class and override un-implemented methods and super constructor

package com.wave.workmanagerexample;


import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;

import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

/**
 * Created on : Mar 26, 2019
 * Author     : AndroidWave
 */
public class NotificationWorker extends Worker {
    private static final String WORK_RESULT = "work_result";


    public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        Data taskData = getInputData();
        String taskDataString = taskData.getString(MainActivity.MESSAGE_STATUS);

        showNotification("WorkManager", taskDataString != null ? taskDataString : "Message has been Sent");

        Data outputData = new Data.Builder().putString(WORK_RESULT, "Jobs Finished").build();

        return Result.success(outputData);

    }

    private void showNotification(String task, String desc) {

        NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);


        String channelId = "task_channel";
        String channelName = "task_name";

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            NotificationChannel channel = new
                    NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), channelId)
                .setContentTitle(task)
                .setContentText(desc)
                .setSmallIcon(R.mipmap.ic_launcher);

        manager.notify(1, builder.build());

    }

}

3. Create WorkRequest

Let’s move to MainActivity and create a WorkRequest to execute the work that we just created. Now first we will create WorkManager. This work manager will enqueue and manage our work request.

 WorkManager mWorkManager = WorkManager.getInstance();

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

OneTimeWorkRequest mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class).build();

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

4. Enqueue the request with WorkManager

Now onClick() of the button we will enqueue this request using the WorkManager. So that’s all you need to do.

      btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mWorkManager.enqueue(mRequest);
            }
        });

5. Fetch the particular task status

Let us fetch some information about this particular task and display it on tvStatus TextView. We will do that using WorkInfo class. The work manager provides LiveData for each of the work request objects, We can observe this and get the current status of the task.

   mWorkManager.getWorkInfoByIdLiveData(mRequest.getId()).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(@Nullable WorkInfo workInfo) {
                if (workInfo != null) {
                    WorkInfo.State state = workInfo.getState();
                    tvStatus.append(state.toString() + "\n");

                }
            }
        });

Finally, MainActivity looks like this.

package com.wave.workmanagerexample;

import android.arch.lifecycle.Observer;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

public class MainActivity extends AppCompatActivity {
    public static final String MESSAGE_STATUS = "message_status";
    TextView tvStatus;
    Button btnSend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvStatus = findViewById(R.id.tvStatus);
        btnSend = findViewById(R.id.btnSend);

        final WorkManager mWorkManager = WorkManager.getInstance();
        final OneTimeWorkRequest mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class).build();
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mWorkManager.enqueue(mRequest);
            }
        });

        mWorkManager.getWorkInfoByIdLiveData(mRequest.getId()).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(@Nullable WorkInfo workInfo) {
                if (workInfo != null) {
                    WorkInfo.State state = workInfo.getState();
                    tvStatus.append(state.toString() + "\n");

                }
            }
        });
    }
}

Conclusion

Now run the application and see our app the up and running. Now click on Send Notification button. the Job status will show on TextView. So this is a basic tutorial of WorkManager. I hope you get a clear idea about WorkManager. In the upcoming article, I will explain How to run a task in a specific condition.

If it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.

Get Solution Code

If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

5 Comments

  1. tvStatus = findViewById(R.id.tvStatus);
    btnSend = findViewById(R.id.btnSend);
    Does not seem to match the layout
     
    mWorkManager.getWorkInfoByIdLiveData(mRequest.getId()).observe(this, new Observer<WorkInfo>() does not like <WorkInfo>, asks for implement “public void update(Observable o, Object arg) { }”
    and do not accept
    @Override   public void onChanged(@Nullable WorkInfo workInfo) {
    What am I doing wrong?
    Thank you for your job and yourhelp
     
    ( I was trying to edit the previous comment but was non possible…)

  2. Thank you for this Work Manager example ..But i am trying send notification through background using work manager but android pie version is not to run work manager is not run in background not send notification through background

  3. Thank you for this Work Manager example ..But i am trying send notification through background using work manager but android pie version is not to run work manager is not run in background not send notification through background

Write A Comment