Android WorkManager vs JobScheduler

AndroidAndroid JobschedulerAndroid Workmanager

Android Problem Overview


Why do we need the new Android WorkManager if we already have a JobScheduler along with a few nifty backports (AndroidJob and FirebaseJobDispatcher) with the same functionality? Does it have any killer-features or something? Because I don't see anything that makes me want to migrate to the yet another scheduler.

Android Solutions


Solution 1 - Android

"WorkManager has a lot of nice features but its main goal is to use the JobScheduler's API on older devices"... Wait, but we already have some backports. What's wrong with them? To cut it short:

  1. FireaseJobDispatcher is fine but it requires Google Play to schedule jobs which isn't good if we're targeting China, for example.

  2. Evernote's AndroidJob is an excellent backport with a lot of functionality. Imho, it was the best choice for scheduling any work. But now the latest version of the library uses the aforementioned WorkManager under the hood. And, unfortunately, sooner or later the library will be deprecated:

> If you start a new project, you should be using WorkManager instead of this library. You should also start migrating your code from this library to WorkManager. At some point in the future this library will be deprecated.

They suggest to switch to the WorkManager because it provides more features and they also give us a short comparison:

|   Feature          | android-job | WorkManager |
| ------------------ | ----------- | ----------- |
| Exact jobs         | Yes         | No          |
| Transient jobs     | Yes         | No          |
| Daily jobs         | Yes         | No          |
| Custom Logger      | Yes         | No          |
| Observe job status | No          | Yes         |
| Chained jobs       | No          | Yes         |
| Work sequences     | No          | Yes         |

Imo, the the last 3 features are very useful and supported only by the WorkManager. So the answer to my last question is yes, it does have some killer-features:

  • No Google Play required
  • Queryable
  • Chainable
  • Opportunistic

To learn more about WorkManager one should definitely watch this talk by Sumir Kataria

P.S. If anyone knows why FirebaseJobDispatcher is actively supported by Google engineers instead of being deprecated write in the comments below :)

Solution 2 - Android

WorkManager uses JobScheduler service to schedule the jobs. If JobScheduler is not supported by the device, then it uses Firebase JobDispatcher service. If Firebase JobDispatcher is not available on the device, it will use AlarmManager and BroadcastReceiver.

So with WorkManager, you don't need to worry about backward compatibility. In addition to this, it allows for defining constraints, which need to be met in order for the job to be run, such as defining network constraints, battery level, charging state and storage level.

It allows task chaining and passing of argument to the job.

http://www.zoftino.com/scheduling-tasks-with-workmanager-in-android

Solution 3 - Android

First, WorkManager is used for work that can be deferrable and require guarantee execution. With backward compatibility in mind JobScheduler only works on API 23+. To avoid having to work on backward compatibility WorkManager does that for you:-

enter image description here

Features of work manager

  • Guaranteed, constraint aware execution
  • Respect system background restriction
  • Quarreable, you can check status i.e. failed, success, etc
  • Chainable, eg work-A depend on work-B -> Work graph
  • Opportunistic, try best to execute as soon as constraints are met, without actually need job scheduler to intervene i.e wakeup the app or wait for JobSheduler to batch your work if your process is up and running
  • Backward compatible, with or without google play service

WorkManager offers compatibility back to API level 14. WorkManager chooses an appropriate way to schedule a background task, depending on the device API level. It might use JobScheduler (on API 23 and higher) or a combination of AlarmManager and BroadcastReceiver

The extended under the hood architecture

enter image description here

Solution 4 - Android

WorkManager just seems like Google's answer to Evernote's Android-Job library, but with a few improvements. It uses JobScheduler, Firebase JobDispatcher and AlarmManager just like Android-Job depending on the device's API level. Their usage of tags looks pretty much the same and assigning constraints to jobs/work are similar enough.

The two features that I am excited about are: being able to chain work and the ability to be opportunistic on work with constraints. The first will allow work (jobs) to be broken up and more modular for me. And with more modular work, each piece of work may have fewer constraints improving the chance they will complete earlier (opportunistic). For example, the majority of processing work can complete before the work with a network constraint needs to be met.

So if you're happy with your current scheduler implementation and the two features I mentioned don't add value, I don't see a huge benefit to making the switch just yet. But if you're writing something new, it'll probably be worth it to use WorkManager.

Solution 5 - Android

In my testing, JobScheduler could continue running a service after the user closes my app but I could find no way to do that with WorkManager.

I tested on a Nexus 5X running Oreo Android 8.0.0 (API 26). I may have just gotten lucky that this device/OS combination could continue the service after killing the app. I believe it might be device specific because of what I read in this answer https://stackoverflow.com/a/52605503/2848676 to https://stackoverflow.com/questions/50682061/android-is-workmanager-running-when-app-is-closed. "You can find a complete list of different OEM behaviors on dontkillmyapp.com."

NOTE: I observed that when the user closed the app, it took several seconds or even minutes for the JobService to restart.

To make JobScheduler launch a service that survives app death, created the service as follows:

    JobScheduler jobScheduler = (JobScheduler)applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
    ComponentName componentName = new ComponentName(applicationContext, ScannerAsJobService.class);
    JobInfo jobInfo = new JobInfo.Builder(JOB_ID, componentName)
            .setPersisted(true)  // relaunch on reboot
            .setMinimumLatency(1)
            .setOverrideDeadline(1)
            .build();
    int result = jobScheduler.schedule(jobInfo);

ScannerAsJobService updates a notification with current information, including whether the service is "in the background" (which I quote because there are different definitions of "background" but this one accurately reflected whether the app was no longer running):

import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;

import androidx.annotation.RequiresApi;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class ScannerAsJobService extends JobService {
    private static final String TAG = "ScannerAsJobService";

    private static final String NOTIFICATION_CHANNEL_ID = "ll_notification_channel";

    final Handler workHandler = new Handler();
    Runnable workRunnable;

    @Override
    public boolean onStartJob(JobParameters params) {
        workRunnable = new Runnable() {
            @Override
            public void run() {
                // See https://stackoverflow.com/questions/45692181/android-job-scheduler-schedule-job-to-execute-immediately-and-exactly-once
                (new Timer()).schedule(new TimerTask() {
                    @Override
                    public void run() {
                        String message = "Time: " + (new Date()).toString() + " background: " + appIsInBackground();

                        // https://stackoverflow.com/a/32346246/2848676
                        Intent intent = new Intent();

                        PendingIntent pendingIntent = PendingIntent.getActivity(ScannerAsJobService.this, 1, intent, 0);

                        Notification.Builder builder = new Notification.Builder(ScannerAsJobService.this);

                        builder.setAutoCancel(false);
                        builder.setTicker("my ticker info");
                        builder.setContentTitle("my content title");
                        builder.setContentText(message);
                        builder.setSmallIcon(R.drawable.my_icon);
                        builder.setContentIntent(pendingIntent);
//                        builder.setOngoing(true);   // optionally uncomment this to prevent the user from being able to swipe away the notification
                        builder.setSubText("my subtext");   //API level 16
                        builder.setNumber(100);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            setupNotificationChannel(NOTIFICATION_CHANNEL_ID);
                            builder.setChannelId(NOTIFICATION_CHANNEL_ID);
                        }
                        builder.build();

                        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                        manager.notify(93423, builder.build());

                        Log.d(TAG + " Notification message: " + message);

                    }
                }, 1 * 1000, 5 * 1000);

                jobFinished(params, true);
            }
        };
        workHandler.post(workRunnable);

        // return true so Android knows the workRunnable is continuing in the background
        return true;
    }

    // https://stackoverflow.com/a/45692202/2848676
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void setupNotificationChannel(String channelId) {
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // The user-visible name of the channel.
        CharSequence name = getString(R.string.my_channel_name);

        // The user-visible description of the channel.
        String description = getString(R.string.my_channel_name);

        int importance = NotificationManager.IMPORTANCE_LOW;

        NotificationChannel mChannel = new NotificationChannel(channelId, name, importance);

        // Configure the notification channel.
        mChannel.setDescription(description);

        mChannel.enableLights(true);
        // Sets the notification light color for notifications posted to this
        // channel, if the device supports this feature.
        mChannel.setLightColor(Color.RED);

        mChannel.enableVibration(true);
        mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});

        mNotificationManager.createNotificationChannel(mChannel);
    }

    /**
     * Source: <a href="https://stackoverflow.com/a/39589149/2848676">https://stackoverflow.com/a/39589149/2848676</a>
     * @return true if the app is in the background, false otherwise
     */
    private boolean appIsInBackground() {
        ActivityManager.RunningAppProcessInfo myProcess = new ActivityManager.RunningAppProcessInfo();
        ActivityManager.getMyMemoryState(myProcess);
        boolean isInBackground = myProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
        return isInBackground;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        // returning true schedules the job for retry (we're using it to implement periodicity since
        // JobInfo.Builder.setPeriodic() didn't work
        return true;
    }
}

Solution 6 - Android

WorkManager sits on top of JobScheduler and AlarmManager. WorkManager picks the right APIs to use, based on conditions like the user's device API level

WorkManager is a simple, but incredibly flexible library that has many additional benefits. These include:

 -Support for both asynchronous one-off and periodic tasks.
 -Support for constraints such as network conditions, storage space, and charging -status 
 -Chaining of complex work requests, including running work in parallel.
 -Output from one work request used as input for the next.
 -Handles API level compatibility back to API level 14.
 -Works with or without Google Play services.
 -Follows system health best practices.
 -LiveData support to easily display work request state in UI.

Solution 7 - Android

In Google docs, WorkManger is a recommended placement for all previous background tasks, including FirebaseJobDispatcher, GcmNetworkManager, and Job Scheduler.

see: https://developer.android.com/topic/libraries/architecture/workmanager

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNikolay KulachenkoView Question on Stackoverflow
Solution 1 - AndroidNikolay KulachenkoView Answer on Stackoverflow
Solution 2 - AndroidArnav RaoView Answer on Stackoverflow
Solution 3 - AndroidEmmanuel MtaliView Answer on Stackoverflow
Solution 4 - AndroidSteve MiskovetzView Answer on Stackoverflow
Solution 5 - AndroidMichael OsofskyView Answer on Stackoverflow
Solution 6 - AndroidT_VView Answer on Stackoverflow
Solution 7 - Androidsanren1024View Answer on Stackoverflow