Architecture Components

WorkManager Constraints | Running Tasks Under Specific Conditions

In the previous blog, I have explained about WorkManager. We understand the needs, uses and implementation of WorkManager. Now I gonna explain, how to running tasks under specific condition using android WorkManager Constraints. You can specify constraint when work should run.

For example, you might want to specify the tasks should run only when device is connected to the charger or if device is ideal or let’s say downloading the file when device is online, The device is connected with WiFi or Mobile Network. So such constraint can be also implemented using the WorkManager.

Let’s take an example and understand how to apply the constraint to running a task under a specific condition. I continuing the previous article demo project. That article, We were generates a notification of button onClick() event. Now we will apply the constraint. Before moving ahead we have to understand which one constraint provide by WorkManager.

Running Tasks Under Specific Conditions Sample App

WorkManager Constraints

WorkManager constraints specify the requirement that needs to be met before being executed task. In other words, you can say constraints specify the condition for running task under that specific condition. These constraints can be related to storage, battery or network.

The followings Constraints provides by WorkManager

The name are suggesting the uses of each.

1. Constraint requiresStorageNotLow()

If you apply this constraint TRUE that the tasks only execute when the storage isn’t low.

Implementation of requiresStorageNotLow()
  /**
   * Constraints
   * If TRUE task execute only when storage's is not low
   */
  mConstraints = new Constraints.Builder().setRequiresStorageNotLow(true).build();
  /**
   * OneTimeWorkRequest with requiresStorageNotLow Constraints
   */
  mRequest =
      new OneTimeWorkRequest.Builder(NotificationWorker.class).setConstraints(mConstraints)
                .build();
Fetch the task status
  /**
   * Fetch the particular task status using request ID
   */
    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");
      }
    }
  });
Enqueue the WorkRequest
/**
   * Enqueue the WorkRequest
   */
    mWorkManager.enqueue(mRequest);

Fetch the task status and Enqueue WorkRequest steps same for all WorkRequest.

2. Constraint requiresBatteryNotLow()

public boolean requiresBatteryNotLow () true mean task execute when the battery isn’t low

Implementation requiresBatteryNotLow()
  /**
   * Constraints
   * If TRUE task execute only when battery isn't low
   */
  mConstraints = new Constraints.Builder().setRequiresBatteryNotLow(true).build();
  /**
   * OneTimeWorkRequest with requiresBatteryNotLow Constraints
   */
  mRequest =
      new OneTimeWorkRequest.Builder(NotificationWorker.class).setConstraints(mConstraints)
                .build();

3. Constraint requiresCharging()

public boolean requiresCharging () is true then task execute while the device is charging

Implementation requiresCharging()
  /**
   * Constraints
   * If TRUE while the device is charging
   */
  mConstraints = new Constraints.Builder().setRequiresCharging(true).build();
  /**
   * OneTimeWorkRequest with requiresCharging Constraints
   */
  mRequest =
      new OneTimeWorkRequest.Builder(NotificationWorker.class).setConstraints(mConstraints)
                .build();

4. Constraint requiresDeviceIdle()

public boolean requiresDeviceIdle () is true means task only execute while the device is idle. This works after API 23.

Implementation requiresDeviceIdle()
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    /**
     * Constraints
     * If TRUE while the  device is idle
     */
    mConstraints = new Constraints.Builder().setRequiresDeviceIdle(true).build();
    /**
     * OneTimeWorkRequest with requiresDeviceIdle Constraints
     */
    mRequest =
        new OneTimeWorkRequest.Builder(NotificationWorker.class).setConstraints(mConstraints)
            .build();
  }

5. Constraint getRequiredNetworkType()

public NetworkType getRequiredNetworkType (), You can you can specify the NetworkType.

NetworkType is
  • CONNECTED – Any working network connection is required for this task
  • METERED – A metered network connection is required for this task.
  • NOT_REQUIRED – A network is not required for this task. 
  • NOT_ROAMING – A non-roaming network connection is required for this task. 
  • UNMETERED – An unmetered network connection is required for this task.

Implementation getRequiredNetworkType()

  /**
   * Constraints
   * Network type is conneted
   */
  mConstraints =
      new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
  /**
   * OneTimeWorkRequest with requiredNetworkType Connected Constraints
   */
  mRequest =
      new OneTimeWorkRequest.Builder(NotificationWorker.class).setConstraints(mConstraints)
                .build();

Constraint Implementation Demo

Let move to AndroidStudio, and open our previous articles project. We build all type constraint using Constraints.Builder class. Let open the activity_main.xml and add for more button for all type constraints request.

<?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/tvStatus"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="8dp"
      android:gravity="center"
      android:text="Result display here"
      android:textSize="18sp"
      app:layout_constraintBottom_toTopOf="@+id/btnSend"
      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/btnSend"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="16dp"
      android:background="@color/colorAccent"
      android:paddingLeft="8dp"
      android:paddingRight="8dp"
      android:text="Send Notification without Constraints"
      android:textAllCaps="false"
      android:textColor="#fff"

      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/tvStatus"
      />

  <Button
      android:id="@+id/buttonStorageNotLow"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:background="@color/colorAccent"
      android:text="Send Notification with requiresStorageNotLow()"
      android:textAllCaps="false"
      android:textColor="#fff"
      app:layout_constraintBottom_toTopOf="@+id/buttonBatteryNotLow"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/btnSend"
      />

  <Button
      android:id="@+id/buttonBatteryNotLow"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:background="@color/colorAccent"
      android:text="Send Notification with requiresBatteryNotLow()"
      android:textAllCaps="false"
      android:textColor="#fff"
      app:layout_constraintBottom_toTopOf="@+id/buttonRequiresCharging"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/buttonStorageNotLow"
      />

  <Button
      android:id="@+id/buttonRequiresCharging"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:background="@color/colorAccent"
      android:text="Send Notification with requiresCharging()"
      android:textAllCaps="false"
      android:textColor="#fff"
      app:layout_constraintBottom_toTopOf="@+id/buttonDeviceIdle"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/buttonBatteryNotLow"
      />

  <Button
      android:id="@+id/buttonDeviceIdle"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:background="@color/colorAccent"
      android:text="Send Notification with requiresDeviceIdle()"
      android:textAllCaps="false"
      android:textColor="#fff"
      app:layout_constraintBottom_toTopOf="@+id/buttonNetworkType"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/buttonRequiresCharging"
      />

  <Button
      android:id="@+id/buttonNetworkType"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginBottom="32dp"
      android:layout_marginEnd="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:background="@color/colorAccent"
      android:text="Send Notification with getRequiredNetworkType()"
      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/buttonDeviceIdle"
      />

</android.support.constraint.ConstraintLayout>

Open the MainActivity and setOnClick listener of each button.

After adding button in activity_main.xml, In MainActivity find the button ids using findViewById() , You can use data binding also. Here we have takes 5 button for all possible constraint. So let’s create OneTimeWorkRequest for each constraint.

The Full Source Code of MainActivity

package com.wave.workmanagerexample;

import android.arch.lifecycle.Observer;
import android.os.Build;
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.Constraints;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  public static final String MESSAGE_STATUS = "message_status";
  TextView tvStatus;
  Button btnSend, btnStorageNotLow, btnBatteryNotLow, btnRequiresCharging, btnDeviceIdle,
      btnNetworkType;
  OneTimeWorkRequest mRequest;
  WorkManager mWorkManager;

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

  private void initViews() {
    tvStatus = findViewById(R.id.tvStatus);
    btnSend = findViewById(R.id.btnSend);
    btnStorageNotLow = findViewById(R.id.buttonStorageNotLow);
    btnBatteryNotLow = findViewById(R.id.buttonBat