Check if WorkManager is scheduled already
Asked Answered
R

7

61

How can I check if WorkManager is scheduled already.

Here is my code that schedule WorkManager.

public static void scheduleWork() {
    PeriodicWorkRequest.Builder photoCheckBuilder =
            new PeriodicWorkRequest.Builder(WorkManagerService.class, TIME_INTERVAL_IN_SECONDS,
                    TimeUnit.SECONDS);
    PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build();
    WorkManager instance = WorkManager.getInstance();
    if (instance != null) {
        instance.enqueueUniquePeriodicWork("TAG", ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
    }
}

I call scheduleWork() in onCreate() of my Application class. Even I can check my service is running or not by this method. But I don't want schedule WorkManager if it is already scheduled to remove inconsistency in scheduled time.

Like

if(!workManagerIsScheduled())
   {
     scheduleWork();
   }

Any solutions?

Rebarbative answered 31/7, 2018 at 11:25 Comment(6)
You can get status of work request...to checkEquiponderance
Did you try that?Equiponderance
See answer @EquiponderanceRebarbative
there is no need to check if worker is already running since you are using instance.enqueueUniquePeriodicWork() with 'ExistingPeriodicWorkPolicy.KEEP'. It will run the new PeriodicWorkRequest only if there is no pending work labelled with uniqueWorkName.Omaomaha
'ExistingPeriodicWorkPolicy.KEEP' is for a particular task, not for work. If some service is doing its work then this flag keep service running till it finish it's work.Rebarbative
Right, that's what I was trying to say...@Khemraj, good...Equiponderance
R
118

Update

If you need to check already running work manager just because you don't want duplicate works. You can simply use enqueueUniquePeriodicWork()

This method allows you to enqueue a uniquely-named PeriodicWorkRequest, where only one PeriodicWorkRequest of a particular name can be active at a time. For example, you may only want one sync operation to be active. If there is one pending, you can choose to let it run or replace it with your new work.

So you don't need to worry about duplicacy about works.

 workmanager.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
  • Where TAG is unique name by which work manager will check duplicacy.
  • You can choose between ExistingPeriodicWorkPolicy.KEEP and ExistingPeriodicWorkPolicy.REPLACE.

Orignal Post

I created this method when I did not find any.

Check if work is running by TAG

if (your_work_manager.version >= 1.0.0-alpha11)

private boolean isWorkScheduled(String tag) {
    WorkManager instance = WorkManager.getInstance();
    ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosByTag(tag);
    try {
        boolean running = false;
        List<WorkInfo> workInfoList = statuses.get();
        for (WorkInfo workInfo : workInfoList) {
            WorkInfo.State state = workInfo.getState();
            running = state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;
        }
        return running;
    } catch (ExecutionException e) {
        e.printStackTrace();
        return false;
    } catch (InterruptedException e) {
        e.printStackTrace();
        return false;
    }
}

if (your_work_manager.version < 1.0.0-alpha11)

private boolean isWorkScheduled(String tag) {
    WorkManager instance = WorkManager.getInstance();
    LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
    if (statuses.getValue() == null) return false;
    boolean running = false;
    for (WorkStatus workStatus : statuses.getValue()) {
        running = workStatus.getState() == State.RUNNING | workStatus.getState() == State.ENQUEUED;
    }
    return running;
}

It will return true when some of its task is RUNNING or ENQUEUED.

Sample code

public static final String TAG_MY_WORK = "mywork";

if(!isWorkScheduled(TAG_MY_WORK)) { // check if your work is not already scheduled
    scheduleWork(TAG_MY_WORK); // schedule your work
}

public static void scheduleWork(String tag) {
    PeriodicWorkRequest.Builder photoCheckBuilder =
            new PeriodicWorkRequest.Builder(WorkManagerService.class, TIME_INTERVAL_IN_SECONDS,
                    TimeUnit.SECONDS);
    PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build();
    WorkManager instance = WorkManager.getInstance();
    instance.enqueueUniquePeriodicWork(tag, ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
}
Rebarbative answered 31/7, 2018 at 12:11 Comment(8)
Hello Khemraj your solution give me this type of error androidx.work.impl.utils.futures.SettableFuture cannot be cast to android.arch.lifecycle.LiveData It can not be cast LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);Smolder
If you wish to get livedata status then you have to change "getStatusesByTag(tag)" to "getStatusesByTagLiveData(tag)" as per new API.Flameout
What about State.BLOCKED ?. Why It is not used for checking? developer.android.com/topic/libraries/architecture/workmanager/…. It explains a task is waiting for other tasks to finish which is supposed to run in order. Ideally, shouldn't we use !state.isFinished() to check any task still queued or running or waiting to run.Bloodletting
When checking instance.getWorkInfosByTag(tag) always returns empty List so i used instance.getWorkInfosForUniqueWork(tag) is working fine in dependencies -> implementation "androidx.work:work-runtime:2.2.0"Waver
You should use running |= state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED; otherwise, running will have the value of the last workInfoVerdie
There is no way to get a list of all Workers?Perfunctory
Above code is not working. @SarathKumar you are definitely right! We should use your method to let it work.Hypotaxis
For using ListenableFeature you need to add guava to your project... issuetracker.google.com/issues/300760566Welbie
E
9

The accepted answer is wrong (badly, as it fails silently). Here is a correct answer

private boolean isWorkScheduled(String tag, Context context) {

        WorkManager instance = WorkManager.getInstance(context);
        ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosByTag(tag);

        boolean running = false;
        List<WorkInfo> workInfoList = Collections.emptyList(); // Singleton, no performance penalty

        try {
            workInfoList = statuses.get();
        } catch (ExecutionException e) {
            Log.d(TAG, "ExecutionException in isWorkScheduled: " + e);
        } catch (InterruptedException e) {
            Log.d(TAG, "InterruptedException in isWorkScheduled: " + e);
        }

        for (WorkInfo workInfo : workInfoList) {
            WorkInfo.State state = workInfo.getState();
            running = running || (state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED);
        }
        return running;

Beside some refactoring to avoid misleading multiple returns, the bug is in this line

running = state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;

If you use this line, you will only get the last evaluated running. A comment propose to use the operator =| instead. Even if the result will be correct, the code will be unclear and (slightly) suboptimal. | is a bitwise operator and || is a logical operator. The operation we want to perform is logical, not bitwise. On booleans, | and || give the same result, but only || is shortcut, which is the intended behavior in our case.

This code works for at least WorkManager 2.5.0 and 2.6.0.

Equivocate answered 6/8, 2021 at 13:31 Comment(0)
S
6

from 1.0.0-alpha11 along with many things WorkStatus will not work it's removed and it's a breaking change. Check Release Notes

WorkStatus has been renamed to WorkInfo. All corresponding getStatus method variants have been renamed to the corresponding getWorkInfo variants. This is a breaking change.

after updating to alpha11 the working code is.

private boolean isWorkScheduled(List<WorkInfo> workInfos) {

    boolean running = false;

    if (workInfos == null || workInfos.size() == 0) return false;

    for (WorkInfo workStatus : workInfos) {
        running = workStatus.getState() == WorkInfo.State.RUNNING | workStatus.getState() == WorkInfo.State.ENQUEUED;
    }

    return running;
}
Sis answered 12/11, 2018 at 12:11 Comment(0)
A
3

Since October 11 (2018) you can use ListenableFuture instead of SynchronousWorkManager which was removed:

You can now synchronously get and observe by using ListenableFutures. For example, WorkManager.enqueue() used to return void; it now returns a ListenableFuture. You can call ListenableFuture.addListener(Runnable, Executor) or ListenableFuture.get() to run code once the operation is complete.

More info can be found here.

Another option can be using ListenableWorker:

A class that can perform work asynchronously in WorkManager. For most cases, we recommend using Worker, which offers a simple synchronous API that is executed on a pre-specified background thread.

It returns ListenableFuture after you call startWork() function.

More info can be found here.

Andreandrea answered 22/9, 2018 at 12:28 Comment(4)
I can't seem to resolve SynchronousWorkManager in my project... has it been dropped?Symbolism
Yes it seems to be gone since October 11. You can use ListenableFuture instead.Andreandrea
Since this answer is obsolete because of removed SynchronousWorkManager you might consider deleting it.Welldisposed
@Welldisposed removed obsolete info and added some more information about ListenableWorker.Andreandrea
S
2

I found the answer in this post and I made some editing.

In my project; i'm sending location info to server every 15 minutes. And i dont want to schedule WorkManager if it is already scheduled.

// Fırst I'm creating workRequest here.
   private void createWorkRequest() {
        Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder
                (LocationWorker.class, 15, TimeUnit.MINUTES)
                .setConstraints(constraints)
                .build();
        WorkManager.getInstance(this)
                .enqueueUniquePeriodicWork("sendLocation", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
    }

// Then i'm learning the state of Work
    private WorkInfo.State getStateOfWork() {
        try {
            if (WorkManager.getInstance(this).getWorkInfosForUniqueWork("sendLocation").get().size() > 0) {
                return WorkManager.getInstance(this).getWorkInfosForUniqueWork("sendLocation")
                        .get().get(0).getState();
                // this can return WorkInfo.State.ENQUEUED or WorkInfo.State.RUNNING
                // you can check all of them in WorkInfo class.
            } else {
                return WorkInfo.State.CANCELLED;
            }
        } catch (ExecutionException e) {
            e.printStackTrace();
            return WorkInfo.State.CANCELLED;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return WorkInfo.State.CANCELLED;
        }
    }

// If work not ( ENQUEUED and RUNNING ) i'm running the work.
// You can check with other ways. It's up to you.
    private void startServerWork() {
        if (getStateOfWork() != WorkInfo.State.ENQUEUED && getStateOfWork() != WorkInfo.State.RUNNING) {
            createWorkRequest();
            Log.wtf("startLocationUpdates", ": server started");
        } else {
            Log.wtf("startLocationUpdates", ": server already working");
        }
    }
Seine answered 19/11, 2019 at 9:20 Comment(0)
I
1

I think we should check all of the six states of WorkRequest by UniqueWorkName. If work request will fall in any of the state we should mark it scheduled already. You can also remove few of the states in your case to fulfil your business need.

private fun isWorkEverScheduledBefore(context: Context, tag: String): Boolean {
    val instance = WorkManager.getInstance(context)
    val statuses: ListenableFuture<List<WorkInfo>> = instance.getWorkInfosForUniqueWork(tag)
    var workScheduled = false
    statuses.get()?.let {
        for (workStatus in it) {
            workScheduled = (
                        workStatus.state == WorkInfo.State.ENQUEUED
                                || workStatus.state == WorkInfo.State.RUNNING
                                || workStatus.state == WorkInfo.State.BLOCKED
                                || workStatus.state.isFinished // It checks SUCCEEDED, FAILED, CANCELLED already
                    )
        }
    }
    return workScheduled
}
Inaction answered 3/1, 2022 at 9:56 Comment(0)
K
0

If work manager is scheduled

private val _workManagerObserver = WorkManager.getInstance(application)
    .getWorkInfosForUniqueWorkLiveData(TAG)
    .map { it.lastOrNull() }
    .map { it?.state?.isFinished != true }
    .map {
        Timber.d("_workManagerObserver $it")
        it
    }
    .asFlow()

If work manager is running

private val _workManagerObserver = WorkManager.getInstance(application)
    .getWorkInfosForUniqueWorkLiveData(TAG)
    .map { it.lastOrNull() }
    .map { it?.state == WorkInfo.State.RUNNING }
    .map {
        Timber.d("_workManagerObserver $it")
        it
    }
    .asFlow()
Kial answered 18/12, 2022 at 22:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.