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.
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);
}
Related
While trying to find out why my android app (java) was not responding for the first 2-3 seconds after launching (UI elements were loaded, apart from menu options, but I could not click anything), I located the delay being caused by the command that schedules a task with JobScheduler.
Basically I have an App class that extends Application, and there I do set a task with JobScheduler that would run every 30 minutes and perform some action (update some information and provide notification if their value was changed).
After creating the Builder containing all details for that task, i run the command scheduler.schedule(info) and by commenting it out I found out that this is the reason my app is unresponsive for 2-3 seconds after launching.
private void scheduleJob() {
ComponentName componentName = new ComponentName(this, myRecurringTask.class);
JobInfo info = new JobInfo.Builder(JOB_ID, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(1800000)
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.schedule(info);
}
I have tried running it in a Thread hoping that it'll run in background and not freeze the UI while running, among other similar practices, but I have yet to manage to run it in background and not have it affect the UI's responsiveness.
I suspect that the lifecycle of the App class might be the reason of that issue but I'm not experienced enough on being sure about it, or knowing how to overcome it.
Have anyone had any similar issue? And if so, how did you get it resolved?
Thank you
UPDATE 2020/02/18: As a workaround I am now checking first if job is already scheduled and only if it's not I go ahead and schedule again.
When I wrote the scheduling part, I saw in the documentation that it will overwrite the job if it already exists, so I did not worry about it, but since it's affecting app's performance, it makes sense to not do it every single time.
With that being said, I'm still looking for a better solution, that's just a workaround to limit the times it gets frozen, the UI still freezes every time job gets scheduled.
After a lot of frustration trying to overcome the issue I was experiencing, I did a more in-depth troubleshoot where I found out that the reason app freezes is that the first job run is running right when I schedule it.
It makes sense but I never thought of it being the issue.
I have made a new question on my quest to overcome that new issue, being able to skip the first job run...
JobScheduler - How to skip first job run of periodic job?
NOTE: THIS IS NOT A DUPLICATE, PLEASE READ THE QUESTION
I want to run a job every time the device is Charging & ON WIFI. This job has to run at most once every time these conditions are met.
This means that if I leave the phone charging overnight with wifi ON the job should not repeat itself.
Only when I unplug and replug the job is allowed to execute again. Same goes for when I turn wifi off and on.
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true)
//.setPeriodic(TimeUnit.SECONDS.toMillis(10))
.setPersisted(true)
Job scheduler provides methods like setPeriodic but that will run my job every X amount of time. Not really what I want.
The job is not critical, I don't need it to be executed right away after the conditions are met, and I'm also OK with it not executing at all sometimes (meaning it's ok for it not to be run when conditions are met for a short period of time)
Is it possible to achieve this using job scheduler? The documentation on this is pretty scare.
If you're not using setPeriodic, then your job would only run once when your other constraints are set. However, your requirements mean you need to schedule a new job when you leave those conditions - JobScheduler does not offer that API, nor does Android offer any API that does that that also works with Android 8.0's Background Execution Limits (with the exception of continuously running a foreground service).
Basically what I want to do is, when there is an internet connection then collect values from sensors and schedule the next time data is collected from sensors, let's say 5 minutes (this interval depends on the current activity: walking, running...). After 5 minutes, the system checks if there is internet connection:
-if there is then the cycle is repeated (collection, sending to server and scheduling the next data collection)
*if there isn't then the next time the data is collected is scheduled for the next time there is an available internet connection
What I tried using:
1) I wanted to use a broadcast receiver for api<21 and job scheduler for api>=21.
For job scheduler, I can't set requirement available network and make the job periodic at the same time because this way the job runs after the period is over whether the requirement is met or not.
2)I tried to make the job service set the next job after the current one is over but the app ran as if that part of the code didn't exist (it only ran once)
3) I tried to somehow combine job scheduler with alarm manager to make the job scheduler manage the network requirement and the alarm manager manage the periodic aspect of the job. But I failed to implement this and I feel like this isn't the right way to do it.
If someone could guide me to a better way to handle this, I'd really appreciate it because I've spent a long time trying to solve this.
I would recommend a library from Evernote called android-job. It handles all the complexity of choosing JobScheduler on 21+, Firebase/GcmNetworkManager, or AlarmManager and all kinds of other things. It has a pretty robust set of features that should fit your use case.
For Example:
int jobId = new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L)
.setBackoffCriteria(5_000L, JobRequest.BackoffPolicy.EXPONENTIAL)
.setRequiresCharging(true)
.setRequiresDeviceIdle(false)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setExtras(extras)
.setRequirementsEnforced(true)
.setPersisted(true)
.setUpdateCurrent(true)
.build()
.schedule();
this question is just to get some ideas on these concepts to see what people in the industry use these for.
If you can just give a few example uses for each of these, I would greatly appreciate it. I know how they work and did read the google documentation on them but I still seem to be having a hard time picking one over the other. I don't need you to explain to me how they work. I am simply asking for a few of their example uses. How you ended up incorporating them into your apps and why you picked them over the others.
Thanks
AlarmManager
Using the AlarmManager to Schedule Tasks at the System Level
The AlarmManager provides access to system-level alarm services. Using the AlarmManager allows an application to schedule tasks that may need to run or repeat beyond the scope of its lifecycle. This allows the application to perform some function even after the application process or all of its Android components have been cleaned up by the system.
Typically, the AlarmManager is used to fire off a PendingIntent that will start up a Service in the future. The AlarmManager triggers Services based on an elapsed interval or at a specific clock time. Both of these options also have the ability to wake up the device when it is asleep if the alarm is urgent.
The benefits of the AlarmManager come into play when using inexact intervals or times to fire off Services. The Android system tries to batch alarms with similar intervals or times together in order to preserve battery life. By batching alarms from multiple applications, the system can avoid frequent device wake and networking.
One concern to consider while using the AlarmManager is that alarms are wiped out during device reboots. Applications need to register the RECEIVE_BOOT_COMPLETE permission in their Android Manifest and reschedule their alarms in a BroadcastReceiver.
Another concern is that a poorly designed alarm could cause battery drain. While the AlarmManager does have the ability to wake devices and set an exact time for an alarm, the documentation mentions that developers should be wary of these features when performing networking. Aside from draining a device’s battery by avoiding batch alarms, setting an exact time for an application to sync with a server could put high strain on a server if every application installation tries to sync with the server around the same time! This can be avoided by adding some randomness to alarm intervals or times.
AlarmManager is a great candidate for scheduling if an application needs to perform a local event at an exact time or inexact interval. Alarm clock or reminder applications are great examples for AlarmManager usage. However, the documentation discourages using AlarmManager for scheduling network-related tasks. Let’s take a look at some better options for networking.
Job Scheduler
JobScheduler helps perform background work in an efficient way, especially networking. JobServices are scheduled to run based on criteria declared in JobInfo.Builder(). These criteria include performing the JobService only when the device is charging, idle, connected to a network or connected to an unmetered network. JobInfo can also include minimum delays and certain deadlines for performing the JobService. Jobs will queue up in the system to be performed at a later time if none of these criteria are met. The system will also try to batch these jobs together in the same manner that alarms are scheduled in order to save battery life when making a network connection.
Developers might be concerned about a scheduler that frequently delays firing off its JobServices. If jobs are frequently delayed and data stale as a result, it would be nice to know about such things. JobScheduler will return information about the JobService such as if it was rescheduled or failed. JobScheduler has back-off and retry logic for handling these scenarios, or developers could handle those scenarios themselves.
Subclassing JobService requires an override of its onStartJob(JobParams params) and onStopJob(JobParams params) methods. onStartJob() is where callback logic for jobs should be placed, and it runs on the main thread. Developers are responsible for threading when dealing with long running jobs. Return true to onStartJob() if separate thread processing needs to occur, or false if processing can occur on the main thread and there is no more work to be done for this job. Developers must also call jobFinished(JobParameters params, boolean needsReschedule) when the job is complete and determine whether or not to reschedule more jobs. onStopJob() will get called to stop or cleanup tasks when initial JobInfo parameters are no longer met, such as a user unplugging their device if that parameter is a requirement.
There might be a lot to think about when implementing a JobService, but it comes with a lot more flexibility than AlarmManager. Another handy feature is that scheduled jobs persist through system reboots.
There is at least one drawback to using JobScheduler. As of the writing of this post, it’s compatable only with API level 21 and higher. Here you can find the distribution of Android devices running various API levels. While there is technically no backport of JobScheduler, a similar tool is GCM Network Manager.
REFERENCE LINK
I am making an application that makes use of the jobscheduler API.
I want to run a service periodically and when the device is charged. This is the code.
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setPeriodic(3000);
builder.setRequiresCharging(true);
mTestService.scheduleJob(builder.build());
Now when I run this and I unplug the device, the service still runs after 3 secs. There is no effect of setting the setRequiresCharging.
When i comment out builder.setPeriodic(3000), it works perfectly fine. I am not sure as to where I am going wrong.
To identify each job internally, the framework creates a new JobStatus when a new job lands on the scheduler. One of the first things JobStatus does is to check and see if your job has a periodic update interval. If it does, it uses the periodic update interval to determine the latest point in the future that a job must be run, or in other words, if it has a deadline constraint.
One of the criteria for a job to be considered ready to be executed, it that all of its constraints have been satisfied or the deadline of a job has expired. See JobStatus.isReady for more information.
The JobSchedulerService adds several StateControllers used to track when jobs should run and when they must be stopped. One of these controllers is a TimeController, which:
sets an alarm for the next expiring job, and determines whether a
job's minimum delay has been satisfied
To determine if a new alarm should be scheduled, TimeController checks if your job has any time delay or deadline constraints, which all jobs seem to be given if you set a periodic update interval.
I hope that helps you at least understand why your job continues being scheduled despite your battery constraint. But I don't have a solution that can offer a straightforward fix for this at the moment.