Android Jetpack

WorkManager Constraints | Running Tasks Under Specific Conditions

Pinterest LinkedIn Tumblr

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 types of constraint using Constraints. Builder class. Let open the activity_main.xml and add for more buttons for all type constraints requests.

<?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 buttons in activity_main.xml, In MainActivity find the button ids using findViewById(), You can use data binding also. Here we have takes 5 buttons for all possible constraints. 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.buttonBatteryNotLow);
    btnRequiresCharging = findViewById(R.id.buttonRequiresCharging);
    btnDeviceIdle = findViewById(R.id.buttonDeviceIdle);
    btnNetworkType = findViewById(R.id.buttonNetworkType);
    btnSend.setOnClickListener(this);
    btnStorageNotLow.setOnClickListener(this);
    btnBatteryNotLow.setOnClickListener(this);
    btnRequiresCharging.setOnClickListener(this);
    btnDeviceIdle.setOnClickListener(this);
    btnNetworkType.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
    tvStatus.setText("");
    Constraints mConstraints;
    switch (v.getId()) {
      case R.id.btnSend:
        mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class).build();
        break;
      case R.id.buttonStorageNotLow:
        /**
         * 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();

        break;
      case R.id.buttonBatteryNotLow:
        /**
         * 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();
        break;
      case R.id.buttonRequiresCharging:
        /**
         * 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();
        break;
      case R.id.buttonDeviceIdle:
        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();
        }
        break;
      case R.id.buttonNetworkType:
        /**
         * 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();
        break;
      default:
        break;
    }
    /**
     * 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
     */
    mWorkManager.enqueue(mRequest);
  }
}

Create a worker class for Notification

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", "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());
  }
}

Conclusion

Now run the application and see, our app the up and running. Now click each button, For better clarity, you have to use an Android emulator and cross-check constraint. If you have any queries, feel free to ask them in the comment section below. Happy Coding

Read our tutorials series on Android Architecture Components

2 Comments

  1. i did Constraints(While device is chargeing only) at that time Workmanager working fine. After that i was close and open the application again, then unplug from charging , then trigger the work manager (Constraint based work device charging) at that time status showing ENQUEUED status, after that i plug the mobile phone to charging, but status is not changing to SUCCESS . please explain why it is hpaeening. thank you

  2. What happens if the contraint is not satisfied? I would like to show a dialog box that says there is no network connectivty ot low battery when one of the constraints is not met. how to acieve that? Please let me know. thank you.

Write A Comment