android – JobScheduler: controlling delay from constraints being met to job being run-ThrowExceptions

Exception or error:

I’m using JobScheduler to schedule jobs. Mainly I’m using it for the .setRequiredNetworkType() method, which allows you to specify that you only want the job to be scheduled when a network connection (or more specifically an unmetered connection) is established.

I’m using the following pretty straightforward code to schedule my jobs:

PersistableBundle extras = new PersistableBundle();
extras.putInt("anExtraInt", someInt);
int networkConstraint = useUnmetered ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

ComponentName componentName = new ComponentName(context, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
        .setRequiredNetworkType(networkConstraint)
        .setExtras(extras)
        .build();

JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);

So there is just one constraint placed on the scheduling: a network connection (which may be ‘any’ or ‘unmetered’).

Short version of the question

How do I specify a maximum delay from all constraints being met, and actually running the job, e.g. “run the job within 2 seconds of there being a network connection”?

Longer version (with ramblings)

The Problem

What I’m finding is that on some devices, if a job is scheduled during a period in which the network constraint is already satisfied, the job will run immediately (or quickly enough to be perceived as so by the user).

But on other devices, even if a suitable network connection is already available (so that the job could run immediately), there is a significant delay before it actually runs. So if this is in response to a user action, the impression is that nothing has happened and that the app is broken.

Now, I’m well aware that this is probably the intention with JobScheduler… that it’s up to the system to schedule the job to best fit in with other demands, and that there is no guarantee that the job will run immediately when all constraints are satisfied.

But it would be nice to be able to have some control over it, where required. So for jobs that happen on a schedule, without user involvement, giving the system complete control over precise timing is fine and good.

But where the job is in response to a user action, I want the job to run without delay… assuming the network connection is there. (If there is no connection, a message can be displayed that the action will happen when a network connection is restored, and the JobScheduler then takes care of ensuring the job runs when the network is restored.)

setOverrideDeadline() is not a solution?

I can see that JobInfo.Builder does have a setOverrideDeadline() method, which is almost what I want. But that specifies the maximum delay from when the job is scheduled (i.e. run the job in 10 seconds’ time even if all constraints are not met), and not from when all constraints have been satisfied (i.e. run the job within 10 seconds of all constraints being satisfied).

EDIT: and there seems to be an annoying bug which can result in the job being run twice when using setOverrideDeadline(): see here and here.

What about Firebase JobDispatcher?

I see that Firebase JobDispatcher has a Trigger.NOW trigger (“means that the Job should be run as soon as its runtime constraints are satisfied”). Perhaps that’s the way to go if JobSchedulerdoesn’t support this natively? I’ve been put off by Firebase JobDispatcher because it seems like it’s using a sledgehammer to crack a nut… and it appears that Firebase is all about cloud messaging etc, which is very far removed from local task scheduling (which should be an entirely local concern). And it seems to require Google Play services, which again seems completely unnecessary for local task scheduling. Furthermore, if immediate triggering it possible with Firebase, and Firebase just uses JobScheduler for Android L+, then it must surely be possible to do this directly with JobScheduler without relying on Firebase?

EDIT: I’ve now tried this, and even Trigger.NOW doesn’t guarantee an immediate response… in fact I am finding that there is a delay of almost exactly 30 seconds on my device, which is odd.

Failing that…

At present, the only way I can see to ensure immediate execution (if constraints are met) is to not use JobScheduler.

Or maybe do the initial constraints check manually, and run the job with a setOverrideDeadline() of 0 if all constraints are met, otherwise run it without setOverrideDeadline().

It would seem far preferable just to have the ability to control the timing of JobScheduler itself, a bit like you can with the setWindow() method of AlarmManager.

How to solve:

A job scheduler is to schedule jobs: triggering periodically, with delay or with constraints to other jobs.
If you want to fire a job instantly, it doesn’t need to bee scheduled, just launch it.

ConnectivityManager cm =
            (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = cm.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {

        // If there is connectivity, Launch the job directly here

    } else {

        PersistableBundle extras = new PersistableBundle();
        extras.putInt("anExtraInt", someInt);
        int networkConstraint = useUnmetered ?       
        JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

        ComponentName componentName = new ComponentName(context,MyJobService.class);
        JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
                .setRequiredNetworkType(networkConstraint)
                .setExtras(extras)
                .build();

        JobScheduler jobScheduler = (JobScheduler)      context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(jobInfo);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *