Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android WorkManager doesn't trigger one of the two scheduled workers

I have two periodic workers scheduled in my app where one worker repeats after 24 hours and another in 15 minutes.

Initially on fresh install things work as expected, but after some days I got an issue on 2 devices(out of 5). The 24 hour worker is triggered properly but the 15 minute one isn't triggered at all. I have been monitoring this for 24 hours now.

I viewed the databse of workmanager via Stetho and saw some entries for 24-hour worker and 0 entries for 15 minute worker. I'm looking in the WorkSpec table.

I debugged via Android studio and after querying WorkManager using getWorkInfosByTag() I got a list of 80 objects for the 15-minute worker where 79 were in CANCELED state and one was in ENQUEUED state.

So apparently, canceled workers are not added to the DB?

I did not find any document from Google which explains the scenarios in which worker is canceled.

I am using 1.0.0-beta03 version of the work runtime. Also, I am not killing the app or doing anything funny. The app is running in the background and not being killed. Devices are Mi A2 (Android 9), Redmi Note 4(Android 7).

I need to understand why is the worker being canceled and is there any better way to debug this? Any pointers will be helpful and upvoted!

Thanks.

Edit1: Posting the code to schedule both workers.

24-hour periodic worker:

public static synchronized void scheduleWork() {
    checkPreviousWorkerStatus();
    if (isWorking()) {
        Log.i("AppDataCleanupWorker", "Did not schedule data cleanup work; already running.");
        return;
    }

    if (lastWorkId != null) {
        WorkManager.getInstance().cancelAllWorkByTag("AppDataCleanupWorker");
        lastWorkId = null;
    }

    Constraints constraints = new Constraints.Builder()
            .setRequiresCharging(true)
            .build();

    PeriodicWorkRequest.Builder builder = new PeriodicWorkRequest
            .Builder(AppDataCleanupWorker.class, 24, TimeUnit.HOURS)
            .addTag("AppDataCleanupWorker")
            .setConstraints(constraints);

    PeriodicWorkRequest workRequest = builder.build();
    lastWorkId = workRequest.getId();
    WorkManager.getInstance().enqueue(workRequest);

    List<WorkInfo> workInfos = WorkManager.getInstance()
            .getWorkInfosByTagLiveData("AppDataCleanupWorker")
            .getValue();
    if (workInfos != null && workInfos.size() > 1) {
        throw new RuntimeException("Multiple workers scheduled. Only one schedule is expected.");
    }
}

15-minute periodic worker:

public static synchronized void scheduleWork() {
    checkPreviousWorkerStatus();
    if (isWorking) {
        Log.i("ImageUploadWorker", "Did not schedule image upload work; already running.");
        return;
    }

    if (lastWorkId != null) {
        WorkManager.getInstance().cancelAllWorkByTag("ImageUploadWorker");
        lastWorkId = null;
    }

    Constraints constraints = new Constraints.Builder()
            .setRequiresBatteryNotLow(true)
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build();
    PeriodicWorkRequest.Builder builder =
            new PeriodicWorkRequest.Builder(ImageUploadWorker.class, 15,
                    TimeUnit.MINUTES)
                    .addTag("ImageUploadWorker")
                    .setConstraints(constraints);
    PeriodicWorkRequest workRequest = builder.build();
    lastWorkId = workRequest.getId();
    WorkManager.getInstance().enqueue(workRequest);

    List<WorkInfo> workInfos = WorkManager.getInstance()
            .getWorkInfosByTagLiveData("ImageUploadWorker").getValue();
    if (workInfos != null && workInfos.size() > 1) {
        throw new RuntimeException("Multiple workers scheduled. Only one schedule is expected.");
    }
}

Note: The device is connected to the Internet & network speed is pretty good.

like image 881
Prasad Pawar Avatar asked Jan 31 '19 08:01

Prasad Pawar


People also ask

What happens when work is scheduled in the workmanager?

When work is scheduled, the WorkManager will determine the best available alternative to execute scheduled work: the latest available version of the platform Job Scheduler, the Firebase Job Dispatcher, or even the Alarm Manager. The scheduled work is guaranteed to run, even if your app has been terminated or the device has been rebooted.

What is workmanager in Android?

The WorkManager is an Android Architecture Components that provides a rich, backward-compatible way to use the features provided by the platform Job Scheduler. Like the JobScheduler, the WorkManager is intended for work that must be completed even if your app has been closed.

How to schedule tasks in Android jetpack?

Schedule tasks with WorkManager Part of Android Jetpack. WorkManager is an API that makes it easy to schedule reliable, asynchronous tasks that are expected to run even if the app exits or the device restarts.

Can I use workmanager for in-process background work?

WorkManager is not intended for in-process background work that can safely be terminated if the app process goes away or for work that requires immediate execution. Please review the background processing guide to see which solution meets your needs.


1 Answers

SOLVED: Worker not being triggered by WorkManager

Resolved the issue after some debugging. Posting here in case someone runs into the same issue.

So, I was canceling and enqueuing workers again and again. So lets say a worker is scheduled for 11.15 AM today, then I cancel and enqueue again, the 11.15 AM slot was not being given to the newly enqueued worker.

Instead, When the 11.15 AM slot is utilised, the work manager just checks that the scheduled worker was canceled and does not trigger the newly enqueued worker.

This was the behaviour on 3 out of 5 devices we tested on. On 2 devices the newly enqueued worker was properly being triggered.

Now the solution:

  1. Remove all code to schedule your workers.

  2. In the onCreate() of your application, first invoke pruneWork() on WorkManager to remove all piled up cancelled worker schedules. Remember the method returns Operation which will help you check the completion of removal. Before calling pruneWork() you might also call cancelAllWorkByTag() for all your workers to clean up any and all the pending schedules. This method also returns an Operation.

  3. After the work manager schedules are cleared, you can now schedule your PeriodicWorkRequest the way you want. I used enqueueUniquePeriodicWork() to make sure only one instance of worker is running at a time.

Now, my worker is being triggered every 15 minutes properly.

Note that as and when your device sleeps and goes into doze mode, this 15 minute duration will increase.

You can check the work manager database using Stetho library. The table name is WorkSpec where you'll find all the schedules for your workers. And you can stop app execution at some breakpoint and use getWorkInfosByTag() on WorkManager to get a list of schedules and their current status.

like image 153
Prasad Pawar Avatar answered Dec 04 '22 21:12

Prasad Pawar