Most visited

Recently visited

Added in API level 21

JobInfo.Builder

public static final class JobInfo.Builder
extends Object

java.lang.Object
   ↳ android.app.job.JobInfo.Builder


Builder class for constructing JobInfo objects.

Summary

Public constructors

JobInfo.Builder(int jobId, ComponentName jobService)

Initialize a new Builder to construct a JobInfo.

Public methods

JobInfo.Builder addTriggerContentUri(JobInfo.TriggerContentUri uri)

Add a new content: URI that will be monitored with a ContentObserver, and will cause the job to execute if changed.

JobInfo build()
JobInfo.Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)

Set up the back-off/retry policy.

JobInfo.Builder setExtras(PersistableBundle extras)

Set optional extras.

JobInfo.Builder setMinimumLatency(long minLatencyMillis)

Specify that this job should be delayed by the provided amount of time.

JobInfo.Builder setOverrideDeadline(long maxExecutionDelayMillis)

Set deadline which is the maximum scheduling latency.

JobInfo.Builder setPeriodic(long intervalMillis)

Specify that this job should recur with the provided interval, not more than once per period.

JobInfo.Builder setPeriodic(long intervalMillis, long flexMillis)

Specify that this job should recur with the provided interval and flex.

JobInfo.Builder setPersisted(boolean isPersisted)

Set whether or not to persist this job across device reboots.

JobInfo.Builder setRequiredNetworkType(int networkType)

Set some description of the kind of network type your job needs to have.

JobInfo.Builder setRequiresCharging(boolean requiresCharging)

Specify that to run this job, the device needs to be plugged in.

JobInfo.Builder setRequiresDeviceIdle(boolean requiresDeviceIdle)

Specify that to run, the job needs the device to be in idle mode.

JobInfo.Builder setTriggerContentMaxDelay(long durationMs)

Set the maximum total delay (in milliseconds) that is allowed from the first time a content change is detected until the job is scheduled.

JobInfo.Builder setTriggerContentUpdateDelay(long durationMs)

Set the delay (in milliseconds) from when a content change is detected until the job is scheduled.

Inherited methods

From class java.lang.Object

Public constructors

JobInfo.Builder

Added in API level 21
JobInfo.Builder (int jobId, 
                ComponentName jobService)

Initialize a new Builder to construct a JobInfo.

Parameters
jobId int: Application-provided id for this job. Subsequent calls to cancel, or jobs created with the same jobId, will update the pre-existing job with the same id. This ID must be unique across all clients of the same uid (not just the same package). You will want to make sure this is a stable id across app updates, so probably not based on a resource ID.
jobService ComponentName: The endpoint that you implement that will receive the callback from the JobScheduler.

Public methods

addTriggerContentUri

Added in API level 24
JobInfo.Builder addTriggerContentUri (JobInfo.TriggerContentUri uri)

Add a new content: URI that will be monitored with a ContentObserver, and will cause the job to execute if changed. If you have any trigger content URIs associated with a job, it will not execute until there has been a change report for one or more of them.

Note that trigger URIs can not be used in combination with setPeriodic(long) or setPersisted(boolean). To continually monitor for content changes, you need to schedule a new JobInfo observing the same URIs before you finish execution of the JobService handling the most recent changes.

Because because setting this property is not compatible with periodic or persisted jobs, doing so will throw an IllegalArgumentException when build() is called.

The following example shows how this feature can be used to monitor for changes in the photos on a device.

import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

/**
 * Example stub job to monitor when there is a change to photos in the media provider.
 */
public class PhotosContentJob extends JobService {
    // The root URI of the media provider, to monitor for generic changes to its content.
    static final Uri MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/");

    // Path segments for image-specific URIs in the provider.
    static final List<String> EXTERNAL_PATH_SEGMENTS
            = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.getPathSegments();

    // The columns we want to retrieve about a particular image.
    static final String[] PROJECTION = new String[] {
            MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA
    };
    static final int PROJECTION_ID = 0;
    static final int PROJECTION_DATA = 1;

    // This is the external storage directory where cameras place pictures.
    static final String DCIM_DIR = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_DCIM).getPath();

    // A pre-built JobInfo we use for scheduling our job.
    static final JobInfo JOB_INFO;

    static {
        JobInfo.Builder builder = new JobInfo.Builder(JobIds.PHOTOS_CONTENT_JOB,
                new ComponentName("com.example.android.apis", PhotosContentJob.class.getName()));
        // Look for specific changes to images in the provider.
        builder.addTriggerContentUri(new JobInfo.TriggerContentUri(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
        // Also look for general reports of changes in the overall provider.
        builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MEDIA_URI, 0));
        JOB_INFO = builder.build();
    }

    // Fake job work.  A real implementation would do some work on a separate thread.
    final Handler mHandler = new Handler();
    final Runnable mWorker = new Runnable() {
        @Override public void run() {
            scheduleJob(PhotosContentJob.this);
            jobFinished(mRunningParams, false);
        }
    };

    JobParameters mRunningParams;

    // Schedule this job, replace any existing one.
    public static void scheduleJob(Context context) {
        JobScheduler js = context.getSystemService(JobScheduler.class);
        js.schedule(JOB_INFO);
        Log.i("PhotosContentJob", "JOB SCHEDULED!");
    }

    // Check whether this job is currently scheduled.
    public static boolean isScheduled(Context context) {
        JobScheduler js = context.getSystemService(JobScheduler.class);
        List<JobInfo> jobs = js.getAllPendingJobs();
        if (jobs == null) {
            return false;
        }
        for (int i=0; i<jobs.size(); i++) {
            if (jobs.get(i).getId() == JobIds.PHOTOS_CONTENT_JOB) {
                return true;
            }
        }
        return false;
    }

    // Cancel this job, if currently scheduled.
    public static void cancelJob(Context context) {
        JobScheduler js = context.getSystemService(JobScheduler.class);
        js.cancel(JobIds.PHOTOS_CONTENT_JOB);
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("PhotosContentJob", "JOB STARTED!");
        mRunningParams = params;

        // Instead of real work, we are going to build a string to show to the user.
        StringBuilder sb = new StringBuilder();

        // Did we trigger due to a content change?
        if (params.getTriggeredContentAuthorities() != null) {
            boolean rescanNeeded = false;

            if (params.getTriggeredContentUris() != null) {
                // If we have details about which URIs changed, then iterate through them
                // and collect either the ids that were impacted or note that a generic
                // change has happened.
                ArrayList<String> ids = new ArrayList<>();
                for (Uri uri : params.getTriggeredContentUris()) {
                    List<String> path = uri.getPathSegments();
                    if (path != null && path.size() == EXTERNAL_PATH_SEGMENTS.size()+1) {
                        // This is a specific file.
                        ids.add(path.get(path.size()-1));
                    } else {
                        // Oops, there is some general change!
                        rescanNeeded = true;
                    }
                }

                if (ids.size() > 0) {
                    // If we found some ids that changed, we want to determine what they are.
                    // First, we do a query with content provider to ask about all of them.
                    StringBuilder selection = new StringBuilder();
                    for (int i=0; i<ids.size(); i++) {
                        if (selection.length() > 0) {
                            selection.append(" OR ");
                        }
                        selection.append(MediaStore.Images.ImageColumns._ID);
                        selection.append("='");
                        selection.append(ids.get(i));
                        selection.append("'");
                    }

                    // Now we iterate through the query, looking at the filenames of
                    // the items to determine if they are ones we are interested in.
                    Cursor cursor = null;
                    boolean haveFiles = false;
                    try {
                        cursor = getContentResolver().query(
                                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                PROJECTION, selection.toString(), null, null);
                        while (cursor.moveToNext()) {
                            // We only care about files in the DCIM directory.
                            String dir = cursor.getString(PROJECTION_DATA);
                            if (dir.startsWith(DCIM_DIR)) {
                                if (!haveFiles) {
                                    haveFiles = true;
                                    sb.append("New photos:\n");
                                }
                                sb.append(cursor.getInt(PROJECTION_ID));
                                sb.append(": ");
                                sb.append(dir);
                                sb.append("\n");
                            }
                        }
                    } catch (SecurityException e) {
                        sb.append("Error: no access to media!");
                    } finally {
                        if (cursor != null) {
                            cursor.close();
                        }
                    }
                }

            } else {
                // We don't have any details about URIs (because too many changed at once),
                // so just note that we need to do a full rescan.
                rescanNeeded = true;
            }

            if (rescanNeeded) {
                sb.append("Photos rescan needed!");
            }
        } else {
            sb.append("(No photos content)");
        }
        Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();

        // We will emulate taking some time to do this work, so we can see batching happen.
        mHandler.postDelayed(mWorker, 10*1000);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        mHandler.removeCallbacks(mWorker);
        return false;
    }
}

Parameters
uri JobInfo.TriggerContentUri: The content: URI to monitor.
Returns
JobInfo.Builder

build

Added in API level 21
JobInfo build ()

Returns
JobInfo The job object to hand to the JobScheduler. This object is immutable.

setBackoffCriteria

Added in API level 21
JobInfo.Builder setBackoffCriteria (long initialBackoffMillis, 
                int backoffPolicy)

Set up the back-off/retry policy. This defaults to some respectable values: {30 seconds, Exponential}. We cap back-off at 5hrs. Note that trying to set a backoff criteria for a job with setRequiresDeviceIdle(boolean) will throw an exception when you call build(). This is because back-off typically does not make sense for these types of jobs. See jobFinished(android.app.job.JobParameters, boolean) for more description of the return value for the case of a job executing while in idle mode.

Parameters
initialBackoffMillis long: Millisecond time interval to wait initially when job has failed.
backoffPolicy int: is one of BACKOFF_POLICY_LINEAR or BACKOFF_POLICY_EXPONENTIAL
Returns
JobInfo.Builder

setExtras

Added in API level 21
JobInfo.Builder setExtras (PersistableBundle extras)

Set optional extras. This is persisted, so we only allow primitive types.

Parameters
extras PersistableBundle: Bundle containing extras you want the scheduler to hold on to for you.
Returns
JobInfo.Builder

setMinimumLatency

Added in API level 21
JobInfo.Builder setMinimumLatency (long minLatencyMillis)

Specify that this job should be delayed by the provided amount of time. Because it doesn't make sense setting this property on a periodic job, doing so will throw an IllegalArgumentException when build() is called.

Parameters
minLatencyMillis long: Milliseconds before which this job will not be considered for execution.
Returns
JobInfo.Builder

setOverrideDeadline

Added in API level 21
JobInfo.Builder setOverrideDeadline (long maxExecutionDelayMillis)

Set deadline which is the maximum scheduling latency. The job will be run by this deadline even if other requirements are not met. Because it doesn't make sense setting this property on a periodic job, doing so will throw an IllegalArgumentException when build() is called.

Parameters
maxExecutionDelayMillis long
Returns
JobInfo.Builder

setPeriodic

Added in API level 21
JobInfo.Builder setPeriodic (long intervalMillis)

Specify that this job should recur with the provided interval, not more than once per period. You have no control over when within this interval this job will be executed, only the guarantee that it will be executed at most once within this interval. Setting this function on the builder with setMinimumLatency(long) or setOverrideDeadline(long) will result in an error.

Parameters
intervalMillis long: Millisecond interval for which this job will repeat.
Returns
JobInfo.Builder

setPeriodic

Added in API level 24
JobInfo.Builder setPeriodic (long intervalMillis, 
                long flexMillis)

Specify that this job should recur with the provided interval and flex. The job can execute at any time in a window of flex length at the end of the period.

Parameters
intervalMillis long: Millisecond interval for which this job will repeat. A minimum value of getMinPeriodMillis() is enforced.
flexMillis long: Millisecond flex for this job. Flex is clamped to be at least getMinFlexMillis() or 5 percent of the period, whichever is higher.
Returns
JobInfo.Builder

setPersisted

Added in API level 21
JobInfo.Builder setPersisted (boolean isPersisted)

Set whether or not to persist this job across device reboots. This will only have an effect if your application holds the permission RECEIVE_BOOT_COMPLETED. Otherwise an exception will be thrown.

Parameters
isPersisted boolean: True to indicate that the job will be written to disk and loaded at boot.
Returns
JobInfo.Builder

setRequiredNetworkType

Added in API level 21
JobInfo.Builder setRequiredNetworkType (int networkType)

Set some description of the kind of network type your job needs to have. Not calling this function means the network is not necessary, as the default is NETWORK_TYPE_NONE. Bear in mind that calling this function defines network as a strict requirement for your job. If the network requested is not available your job will never run. See setOverrideDeadline(long) to change this behaviour.

Parameters
networkType int
Returns
JobInfo.Builder

setRequiresCharging

Added in API level 21
JobInfo.Builder setRequiresCharging (boolean requiresCharging)

Specify that to run this job, the device needs to be plugged in. This defaults to false.

Parameters
requiresCharging boolean: Whether or not the device is plugged in.
Returns
JobInfo.Builder

setRequiresDeviceIdle

Added in API level 21
JobInfo.Builder setRequiresDeviceIdle (boolean requiresDeviceIdle)

Specify that to run, the job needs the device to be in idle mode. This defaults to false.

Idle mode is a loose definition provided by the system, which means that the device is not in use, and has not been in use for some time. As such, it is a good time to perform resource heavy jobs. Bear in mind that battery usage will still be attributed to your application, and surfaced to the user in battery stats.

Parameters
requiresDeviceIdle boolean: Whether or not the device need be within an idle maintenance window.
Returns
JobInfo.Builder

setTriggerContentMaxDelay

Added in API level 24
JobInfo.Builder setTriggerContentMaxDelay (long durationMs)

Set the maximum total delay (in milliseconds) that is allowed from the first time a content change is detected until the job is scheduled.

Parameters
durationMs long: Delay after initial content change, in milliseconds.
Returns
JobInfo.Builder

setTriggerContentUpdateDelay

Added in API level 24
JobInfo.Builder setTriggerContentUpdateDelay (long durationMs)

Set the delay (in milliseconds) from when a content change is detected until the job is scheduled. If there are more changes during that time, the delay will be reset to start at the time of the most recent change.

Parameters
durationMs long: Delay after most recent content change, in milliseconds.
Returns
JobInfo.Builder

Hooray!