MobileAndroidHow to Schedule Background Tasks in Android Apps Using the JobScheduler API

How to Schedule Background Tasks in Android Apps Using the JobScheduler API

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Android provides programmatic support to schedule tasks in the foreground as well as the background. One of the APIs available for use to schedule background tasks is the JobScheduler API and we will learn about this API in this article.

Basics

The JobScheduler class resides in the android.app.job namespace and the API provides an interface for scheduling jobs that execute in the application’s own process in the background. This means that the jobs do not run as part of the system; instead, they run in the context of the application.

By default, any network calls in an Android application are done ASAP and use battery power. However, there are certain scenarios that are good candidates for background tasks because there is no urgency for them and does not require using precious resources like battery and network radio.

  • Uploading user data: When the application involved uploading user data that is not time-sensitive.
  • Downloading content: When the application can download information that is not urgent; for example, refresh a TV programming guide.

In such situations, using the JobScheduler API can help. Candidate jobs for JobScheduler need to be self-contained; in other words application behavior should not be impacted if the task cannot be completed instantaneously and in one go.

Prior to the availability of the JobScheduler class, app developers had use Handlers and AlarmManagers, but both of them were limited in ability. Handlers and AlarmManagers did not provide persistence, and did not have a good failure handling story. App developers needed to work with SyncManager to avoid such issues.

Now, with the JobScheduler class, all that developer pain is gone.

What’s Different with JobScheduler

JobScheduler supports a couple of modes that can help with scheduling jobs.

  • Device-idle mode: This is the time when the device is idle and is in charging mode.
  • Network activity aware: When the device is connected to Internet over WiFi instead of cellular radio.

How to Set Up the Job

We need to specify the constraints in which the job should execute. Example of constraints are:

  • Run this when the phone is plugged in
  • Run this when on an unmetered connection, but only run for one hour
  • Wait at least 10 minutes to start this job initially, and then whenever the device is idle
  • Every ‘x’ minutes, whenever network is available let me (app) know so that I (app) can do a small health check.

Scheduler only runs the job when all the criteria are satisfied. The only exception is when you specify a deadline. When a defined deadline expires, it overwrites the constraints to execute the job.

Because of the encapsulation that Android system provides, app developers need to implement only two methods. When the job is finished, the task needs to call jobFinished to indicate that the job is complete.

Task1
Figure 1: Android’s encapsulation features
Source: Google IO 2014 notes.

Hands On

Let us create a simple application that uses JobScheduler API.

Fire up Android Studio and click to Start a new Android Project.

Task2
Figure 2: Starting a new Android project

Provide JobSchedulerDemo as the Application Name and Click Next.

Task3
Figure 3: Naming the new application

On the next screen, leave the default values and click Next.

Task4
Figure 4: Leave the default values in place

On the “Add an activity to Mobile” page, choose “Blank Activity”. This creates an application with a single activity.

Task5
Figure 5: Choosing a Blank Activity

We then are prompted to customize the activity. We will leave the default values unchanged.

Task6
Figure 6: Again, leave the default values unchanged

Click Finish to create the project files.

The solution is now created with a default activity. If you open up the activity_main.xml file, you will notice the following:

<RelativeLayoutxmlns:android="http://schemas.android.com/
      apk/res/android"
   xmlns_tools="http://schemas.android.com/
      tools"android:layout_width="match_parent"
   android_layout_height="match_parent"android:paddingLeft=
      "@dimen/activity_horizontal_margin"
   android_paddingRight="@dimen/activity_horizontal_margin"
   android_paddingTop="@dimen/activity_vertical_margin"
   android_paddingBottom="@dimen/activity_vertical_margin
      "tools:context=".MainActivity">

   <TextViewandroid:text="@string/hello_world"
         android_layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView"/>


</RelativeLayout>

We now will add a button. The experience is that when the user clicks the button, the JobScheduler is invoked and a background task is submitted. The changes to add the button are highlighted in the following example.

<RelativeLayoutxmlns:android="http://schemas.android.com/
      apk/res/android"
   xmlns_tools="http://schemas.android.com/tools"
      android_layout_width="match_parent"
   android_layout_height="match_parent"android:paddingLeft=
      "@dimen/activity_horizontal_margin"
   android_paddingRight="@dimen/activity_horizontal_margin"
   android_paddingTop="@dimen/activity_vertical_margin"
   android_paddingBottom="@dimen/activity_vertical_margin"
      tools_context=".MainActivity">

<TextViewandroid:text=”@string/hello_world” android_layout_width=”wrap_content” android_layout_height=”wrap_content” android_id=”@+id/textView”/> <Button android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:text=”ScheduleJob” android:id=”@+id/buttonScheduleJob” android:layout_below=”@+id/textView” android:layout_toRightOf=”@+id/textView” android:layout_toEndOf=”@+id/textView” android:layout_marginTop=”54dp”/> </RelativeLayout>

Before we start coding, we need to set the minimum API version to 21 because some of the APIs we will use today require version 21 as the minimum. Open build.gradle and make the following change.

apply plugin: 'com.android.application'

android {
   compileSdkVersion 22
   buildToolsVersion "22.0.1"

   defaultConfig {
      applicationId "com.example.vipul.
         jobschedulerdemo"
      minSdkVersion 21targetSdkVersion 22
      versionCode 1
      versionName "1.0"
   }
   buildTypes {
      release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile
            ('proguard-android.txt'), 'proguard-rules.pro'
      }
   }
}

dependencies { compile fileTree(dir: ‘libs’, include: [‘*.jar’]) compile ‘com.android.support:appcompat-v7:22.2.1’ }

The first thing we need to do is to create a service that will handle callbacks from the JobScheduler. Whenever a request is scheduled with JobScheduler, it will land on this service’s onStartJob method.

To create the service, add a new Java class called MyService.

Task7
Figure 7: Creating a new class

The default skeleton code is created.

Package com.example.vipul.jobschedulerdemo;

/**
  *Created by vipul on 9/12/2015.
  */
public classMyService{
}

We will change the default definition to extend this class from the JobService class.

public class MyService extends JobService{

We will now implement MyService.

//Myservice.java

package com.example.vipul.jobschedulerdemo;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/**
  * Created by vipul on 9/12/2015.
  */
public class MyService extends JobService {

   // This method is called when the service instance
   // is created
   @Override
   public void onCreate() {
      super.onCreate();
      Log.i("MyService", "myService created");
   }

   // This method is called when the service instance
   // is destroyed
   @Override
   public void onDestroy() {
      super.onDestroy();
      Log.i("MyService", "myService destroyed");
   }

   // This method is called when the scheduled job
   // is started
   @Override
   public boolean onStartJob(JobParameters params) {
      Log.i("MyService", "on start job");
      return true;
   }

   // This method is called when the scheduled job
   // is stopped
   @Override
   public boolean onStopJob(JobParameters params) {
      Log.i("MyService", "on stop job");
      return true;
   }

   MainActivity myMainActivity;

   public void setUICallback(MainActivity activity) {
   myMainActivity = activity;
   }


   // This method is called when the start command
   // is fired
   @Override
   public int onStartCommand(Intent intent, int flags,
         int startId) {
      Messenger callback = intent.getParcelableExtra("messenger");
      Message m = Message.obtain();
      m.what = 2;
      m.obj = this;
      try {
         callback.send(m);
      } catch (RemoteException e) {
         Log.e("MyService", "Error passing service object
            back to activity.");
      }
      return START_NOT_STICKY;
   }

   // Method that schedules the job
   public void scheduleJob(JobInfo build) {
      Log.i("MyService","Scheduling job");
      JobScheduler jobScheduler = (JobScheduler)getSystemService
         (Context.JOB_SCHEDULER_SERVICE);
      jobScheduler.schedule(build);
   }
}

As you can see from the prreceding snippet, we have implemented various methods (the implementation is self-documented).

Next, we need to wire up the activity in the Application manifest.

Open up AndroidManifest.xml and add an entry for our service.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns_android="http://schemas.android.com/apk/res/android"
      package="com.example.vipul.jobschedulerdemo" >

   application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >

      activity
         android:name=".MainActivity"
         android:label="@string/app_name" >
         <intent-filter>
            action android_name="android.intent.action.MAIN" />

            category android_name="android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
      <service android_name="com.example.vipul.jobschedulerdemo.MyService"/>
   </application>

</manifest>

Lastly, we implement the method in MainActivity.java to call our Service and schedule jobs.

We will need to add a couple of member variables.

public class MainActivity extends Activity {

   ComponentName myServiceComponent;
   MyService myService;
   Handler myHandler = new Handler(){
      @Override
      public void handleMessage(Message msg){
         myService = (MyService) msg.obj;
         myService.setUICallback(MainActivity.this);
      }
   };

Next, we update the onCreate callback to start our service that will execute jobs in the background.

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   myServiceComponent = new ComponentName(this, MyService.class);
   Intent myServiceIntent = new Intent(this, MyService.class);
   myServiceIntent.putExtra("messenger", new Messenger(myHandler));
   startService(myServiceIntent);
}

We next implement the click event handle for the button.

public void onClick(View v){
   JobInfo.Builder builder = new JobInfo.Builder(0, myServiceComponent);
   builder.setRequiresCharging(true);
   //builder.setRequiresDeviceIdle(true);
   //builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
   myService.scheduleJob(builder.build());
}

Note that in the preceding snippet, I have commented out the ability to require the device to be idle and on an unmetered network connection (the code can be uncommented if you need this).

Our application is now ready. When we run the application, you will notice that the scheduled job will run when it is scheduled because the emulator is always in the charging mode.

If you run into issues, you can download the sample from the download link at the end of this article.

Summary

In this article, we learned about the basics of JobScheduler and how to use the JobScheduler API to schedule jobs in the background. I hope you have found this information useful.

About the Author

Vipul Patel is a technology geek based in Seattle. He can be reached at vipul.patel@hotmail.com. You can visit his LinkedIn profile at https://www.linkedin.com/pub/vipul-patel/6/675/508.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories