Tips for developing Android JobScheduler Jobs
If your app has to sync some data in the background, you’ll definitely want to use the Android JobScheduler API to schedule a background job.
There are a few helpful libraries that make this task easier, but you can get away with using the JobScheduler API itself.
I work in Xamarin, so my samples are in C#.
Scheduling a Job
The JobScheduler has a very simple API and you’ll probably won’t need more than one or two methods:
1var jobScheduler = (JobScheduler) context.GetSystemService(Context.JobSchedulerService);
2
3var jobSchedulingResult = jobScheduler.Schedule(
4 new JobInfo.Builder(1234, new ComponentName(context, Class.FromType(SyncJobService))) // 1234: is the unqiue ID of this job in your app - SyncJobService is the type of your JobService
5 .SetPeriodic(1000*60*15) // in ms
6 .SetRequiredNetworkType(NetworkType.Any)
7 .Build());
8
9if (jobSchedulingResult == JobScheduler.ResultSuccess)
10{
11 // yay
12}
13else
14{
15 // :(
16}
This method schedules a new job or updates an existing one. There’s one catch: it will cancel any running job with the same ID. Also: Android decides when to run your job. It will try to respect the periodic interval, but your job won’t run more often than every 15 minutes.
So before scheduling our job, we might have to check if the job is already scheduled with the correct parameters so that we don’t reschedule it unnecessarily.
1var pendingJob = jobScheduler.GetPendingJob(1234);
2if (pendingJob == null)
3{
4 // maybe verify the job settings here
5}
A Job can have a couple of parameters (constraints) that the JobScheduler uses to determine when the Job should run:
- Device is idle
- Low battery
- Periodic
- Network type
There are methods
on the JobInfo.Builder
class for each of these parameters.
Implementing the JobService
JobService
is the base class for any job.
1[Service(Permission = PermissionBind)]
2public class SyncJobService : JobService
3{
4 public override bool OnStartJob(JobParameters @params)
5 {
6 StartSync();
7 return false;
8 }
9
10 public override bool OnStopJob(JobParameters @params)
11 {
12 return false;
13 }
14}
When your job has to start, the method OnStartJob
is called. The params
have some information like which network should be used for your job or which constraints triggered the job. Note that this method runs on the main (UI) thread, which is often not what you want. So you should use something like Task.Run(() => StartSync(), TaskCreationOptions.LongRunning)
to make sure it runs on a background thread.
The return value of OnStartJob
indicates if the job will still be running after returning from this method. You have to explicitly call JobFinished(@params, false)
to indicate that the job is finished. The boolean in this method indicates if the job has to be rescheduled (because if failed) and same goes for the boolean in OnStopJob
. The method OnStopJob
is called when your job has to be cancelled.
Combining with BroadcastReceivers
The app I’m building requires the job to be always scheduled, so I used a BroadcastReceiver to schedule the job when the device boots up.
1[assembly: UsesPermission(Manifest.Permission.ReceiveBootCompleted)]
2
3[BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true)]
4[IntentFilter(new[] {Intent.ActionLockedBootCompleted})]
5public class BootedBroadcastReceiver: MvxBroadcastReceiver
6{
7 public override void OnReceive(Context context, Intent intent)
8 {
9 this.RegisterSetupType<Setup>();
10 base.OnReceive(context, intent);
11
12 ScheduleJobs(context);
13 }
14}
This MvvmCross base class makes sure that the Setup
has completed before scheduling your jobs (see “Processes” below). Don’t forget the required permission to receive BootCompleted
intents.
I also schedule the job when the app opens so that users don’t have to reboot their device after installing the app.
Processes
Most of my apps have an IoC container or some services that have to be initialized/restored before the app can do anything. If you use MvvmCross
, you can use MvxAndroidSetupSingleton.EnsureSingletonAvailable(this).EnsureInitialized()
in OnStartJob
to make sure everything is initialized. Note that jobs - by default - run in the same process that scheduled the jobs in the first. If you reschedule your jobs, then that process becomes the host.
So with all of the above, this is what happens:
- Device boots up - BroadcastReceivers schedules the jobs (process 1)
- The JobScheduler executes the job in process 1
- The user opens the app, but Android detects that there’s also a process for this app (process 1)
- As the setup has already ran in this process, the app opens a lot faster
So that makes for a very helpful side effect: app start up is a lot faster since the setup has already completed to make sure that the jobs can run.
There are attributes to mark that your job has to run in an isolated process, but that usually involves a lot more work to make sure that your data (database, local storage) can work with this. Systems like MvxMessenger
also don’t work between multiple processes.
Some helpful commands
You can try and test this on a emulator, but I recommend to use Android 7.1 or newer for this. You can’t force any jobs to run immediately while testing on versions before 7.1.
Monitor when adb becomes available
So that you can connect to logcat as soon as ADB becomes available during the boot process.
while :; do; clear; adb devices; sleep 2; done
Info about scheduled jobs
Use grep to search for the history of executed jobs or for the schedule of your jobs.
adb shell dumpsys jobscheduler
adb shell dumpsys jobscheduler | grep -C 5 your.package.id
adb shell dumpsys jobscheduler | grep -B 10 "Pending queue"
Run a scheduled job instantly
End with your package ID and job ID.
adb shell cmd jobscheduler run -f your.package.id 1234
You might also like
If you liked this article, follow me on LinkedIn or other social media to stay up-to-date with my latest posts. You might also like the following 2 posts about related topics:
Optimize memory usage in Xamarin apps
This post has been translated to Russian by Denis Gordin . You can read the Russian version on the Russian website TechMedia . Thanks, Denis! Xamarin is amazing in how it allows .NET developers to write apps for Android, iOS, MacOS… in C#. But that amazing capability comes with a prize and even the most simple apps can suffer from high memory usage.
Diagnosing memory issues with the Xamarin profiler
The Xamarin profiler is a must-have tool for every Xamarin developer. The profiler comes with a Xamarin business or enterprise license and is available as a standalone installer at xamarin.com/profiler . To get started, make sure a debug version of your app is installed on a device or a simulator (works both for Android and iOS).