FirebaseJobDispatcher

The Firebase alternative to JobScheduler – is able to schedule tasks with complex conditions and execute them when those conditions are met. Provides compatibility all the way back to API 9 (Gingerbread). Requires Google Play Services to be installed on the target phone as it depends on GooglePlayDriver.

Add dependencies to build.gradle:

implementation 'com.firebase:firebase-jobdispatcher:0.8.5'

Create a JobService class and add the 2 obligatory overrides:

public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters job) {
        // Do some work here
        return false; // Answers the question: "Is there still work going on?"
    }

    @Override
    public boolean onStopJob(JobParameters job) { // This method is called if the system has determined that you must stop execution of your job even before you've had a chance to call jobFinished
        return false; // Answers the question: "Should this job be retried?"
    }
}

As the JobService runs on the main thread, potentially slow processes should be handed off to an AsyncTask. So alternatively:

public class MyJobService extends JobService {
    private AsyncTask mNetworkTask;
    @Override
    public boolean onStartJob(final JobParameters job) {
        mNetworkTask = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                // Do something
                return null;
            }

            @Override
            protected void onPostExecute(Object o) { // When your AsyncTask has finished you must inform the JobService
                jobFinished(job, false); // Second parameter tells the Jobservice whether the task needs to be rescheduled (depends on RetryStrategy, set in FirebaseJobDispatcher code below)
            }
        };
        mNetworkTask.execute();
        return true; // There is still work going on
    }

    @Override
    public boolean onStopJob(JobParameters job) { // This method is called if the system has determined that you must stop execution of your job even before you've had a chance to call jobFinished
        return false; // Answers the question: "Should this job be retried?"
    }
}

Add the service to the AndroidManifest.xml:

<service
    android:exported="false"
    android:name=".MyJobService">
    <intent-filter>
        <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
    </intent-filter>
</service>

The actual code:

FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); // Create a new dispatcher using the Google Play driver
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");

Job myJob = dispatcher.newJobBuilder()
    .setService(MyJobService.class) // the JobService that will be called - ESSENTIAL
    .setTag("my-unique-tag") // uniquely identifies the job - ESSENTIAL
    .setRecurring(false) // one-off job
    .setLifetime(Lifetime.UNTIL_NEXT_BOOT) // don't persist past a device reboot
    .setTrigger(Trigger.executionWindow(0, 60)) // start between 0 and 60 seconds from now
    .setReplaceCurrent(false) // don't overwrite an existing job with the same tag
    .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) // retry with exponential backoff
    .setConstraints( // constraints that need to be satisfied for the job to run
        Constraint.ON_UNMETERED_NETWORK, // only run on an unmetered network
        Constraint.DEVICE_CHARGING // only run when the device is charging
    )
    .setExtras(myExtrasBundle)
    .build();

dispatcher.mustSchedule(myJob); // Throws exception if fails (ScheduleFailedException)
OR
dispatcher.schedule(myJob); // Returns one of (int value) SCHEDULE_RESULT_SUCCESS, SCHEDULE_RESULT_UNKNOWN_ERROR, SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, SCHEDULE_RESULT_UNSUPPORTED_TRIGGER, SCHEDULE_RESULT_BAD_SERVICE

To cancel a job:

dispatcher.cancel("my-unique-tag");

To cancel all jobs:

dispatcher.cancelAll();

ud851-Exercises-student\Lesson10-Hydration-Reminder\T10.04

JobScheduler

Beginning with Android Lollipop, JobScheduler is able to schedule tasks with complex conditions and execute them when those conditions are met. Consider FirebaseJobDispatcher instead for greater backwards compatibility.

JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); // Get a JobScheduler
JobInfo job = new JobInfo.Builder( // Build a new JobInfo object
  MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) // Point to your JobService class
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // Only trigger when on an unmetered network e.g. Wifi
    .setRequiresCharging(true) // Only trigger when being charged
    .setBackOffCriteria(TWO_MINUTES,BACKOFF_POLICY_EXPONENTIAL) // After first fail, wait 2 minutes, and increase wait time exponentially with every subsequent fail
    .setMinimumLatency(FIFTEEN_MINUTES) // Minimum wait before triggering is 15 minutes
.build();

js.schedule(job) // Call the job using this code

Services

Services are Android Framework components meant for running background tasks that don’t need a visual component, therefore they do not provide a User Interface (UI). An Activity can start a Service which can then continue to run, even after the Activity is shut down. They are ideal for loading and processing data in the background, regardless of whether any of the app’s Activities are open. A common example of this is how you can receive notifications when an app is not running.

A Service should be used when the task is decoupled from (does not directly affect) the UI, and needs to exist even when there is no UI.

There are 3 ways to start a Service:

  • Start – manually from a context e.g. Activity. Executes but will not normally communicate back to the component that started it.
  • Schedule – if you want to have a Service execute at some point in the future, or when some conditions are met, you can create a JobService. You can control when this starts via a Scheduler e.g. JobScheduler
  • Bind – offers a client-server-like interface, with the Service being the server, and the various components being bound to the Service being the clients. Components bind to the Service via the bindService() method. These Services can easily communicate with the components that are bound to them (unlike the started Services above). An example might be an audio player, where the Service plays the audio while the Activity controls the UI, with the UI being updated as to the progress of the audio file play. Likewise, pressing the Pause button will mean sending a command to the Service.

A Service can be both STARTED and BOUND.

If you want to run a Service in a completely separate thread you can use an Intent Service.

The Lifecycle of a Started Service is depicted below: