1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
# Quick Start Guide to using BackgroundTaskScheduler
## Overview
This document describes how to schedule a background task in Android using
BackgroundTaskScheduler API. Although most of the examples are described in
Java, the exact same API is available in C++ as well.
## Background
In Android it is encouraged to use `JobScheduler` for all background jobs,
instead of using things like `IntentService` or polling using alarms. Using the
system API is beneficial as it has a full view of what goes on in the system and
can schedule jobs accordingly.
The `background_task_scheduler` component provides a framework for use within
Chromium to schedule and execute background jobs using the system API. The
API of the framework is similar to that of the Android `JobScheduler`.
## What is a task
A task is defined as a class that implements the `BackgroundTask` interface,
which looks like this:
```java
interface BackgroundTask {
interface TaskFinishedCallback {
void taskFinished(boolean needsReschedule);
}
boolean onStartTask(Context context,
TaskParameters taskParameters,
TaskFinishedCallback callback);
boolean onStopTask(Context context,
TaskParameters taskParameters);
}
```
**Any class implementing this interface must have a public constructor which takes
no arguments.**
A task must also have a unique ID, and it must be listed in `TaskIds` to ensure
there is no overlap between different tasks.
The connection between `TaskIds` and the corresponding `BackgroundTask` classes is done by injecting
a `BackgroundTaskFactory` class in `BackgroundTaskSchedulerFactory`. For the //chrome embedder
(which is the only one needing the association), the `ChromeBackgroundTaskFactory` [implementation]
(https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser
/background_task_scheduler/ChromeBackgroundTaskFactory.java) was created. Anyone that adds a new
task id to `TaskIds` should add a case in this class to.
## How to schedule a task
A task is scheduled by creating an object containing information about the task,
such as when to run it, whether it requires battery, and other similar
constraints. This object is called `TaskInfo` and has a builder you can use
to set all the relevant fields.
There are three main types of tasks; one-off tasks, periodic tasks, and exact timing info tasks. One-off
tasks are only executed once, whereas periodic tasks are executed once per
a defined interval. The exact info tasks are triggered at the exact scheduled time.
There are two steps in the process of creating a TaskInfo:
1. the specific timing info is created; there are three objects available - `OneOffInfo`,
`PeriodicInfo`, and `ExactInfo`; each one of these objects has its own builder;
2. the task info is created using the `createTask` method; other parameters can be set afterwards.
As an example for how to create a one-off task that executes in 200 minutes,
you can do the following:
```java
TaskInfo.TimingInfo oneOffInfo = TaskInfo.OneOffInfo.create()
.setWindowEndTimeMs(TimeUnit.MINUTES.toMillis(200)).build();
TaskInfo taskInfo = TaskInfo.createTask(TaskIds.YOUR_FEATURE,
oneOffInfo).build();
```
For a periodic task that executes every 200 minutes, you can call:
```java
TaskInfo.TimingInfo periodicInfo = TaskInfo.PeriodicInfo.create()
.setIntervalMs(TimeUnit.MINUTES.toMillis(200)).build();
TaskInfo taskInfo = TaskInfo.createTask(TaskIds.YOUR_FEATURE,
periodicInfo).build();
```
Typically you will also set other required parameters such as what type of
network conditions are necessary and whether the task requires the device to
be charging. They can be set on the builder like this:
```java
TaskInfo.TimingInfo oneOffInfo = TaskInfo.OneOffInfo.create()
.setWindowStartTimeMs(TimeUnit.MINUTES.toMillis(100))
.setWindowEndTimeMs(TimeUnit.MINUTES.toMillis(200)).build();
TaskInfo taskInfo = TaskInfo.createTask(TaskIds.YOUR_FEATURE,
oneOffInfo)
.setRequiresCharging(true)
.setRequiredNetworkType(
TaskInfo.NETWORK_TYPE_UNMETERED)
.build();
```
Note that the task will be run after `windowEndTimeMs` regardless of whether the
prerequisite conditions are met. To work around this, mark the `windowEndTimeMs`
to `Integer.MAX_VALUE`.
When the task is ready for scheduling, you use the
`BackgroundTaskSchedulerFactory` to get the current instance of the
`BackgroundTaskScheduler` and use it to schedule the job.
```java
BackgroundTaskSchedulerFactory.getScheduler().schedule(myTaskInfo);
```
If you ever need to cancel a task, you can do that by calling `cancel`, and
passing in the task ID:
```java
BackgroundTaskSchedulerFactory.getScheduler().cancel(TaskIds.YOUR_FEATURE);
```
## Passing task arguments
A `TaskInfo` supports passing in arguments through a `Bundle`, but only values
that can be part of an Android `BaseBundle` are allowed. You can pass them in
using the `TaskInfo.Builder`:
```java
Bundle myBundle = new Bundle();
myBundle.putString("foo", "bar");
myBundle.putLong("number", 1337L);
TaskInfo.TimingInfo oneOffInfo = TaskInfo.OneOffInfo.create()
.setWindowStartTimeMs(TimeUnit.MINUTES.toMillis(100))
.setWindowEndTimeMs(TimeUnit.MINUTES.toMillis(200)).build();
TaskInfo taskInfo = TaskInfo.createTask(TaskIds.YOUR_FEATURE,
oneOffInfo)
.setExtras(myBundle)
.build();
```
These arguments will be readable for the task through the `TaskParameters`
object that is passed to both `onStartTask(...)` and `onStopTask(...)`, by
doing the following:
```java
boolean onStartTask(Context context,
TaskParameters taskParameters,
TaskFinishedCallback callback) {
Bundle myExtras = taskParameters.getExtras();
// Use |myExtras|.
...
}
```
For native tasks, the extras are packed into a std::string, It's the caller's
responsibility to pack and unpack the task extras correctly into the std::string.
We recommend using a proto for consistency.
## Performing actions over TimingInfo objects
To perform actions over the `TimingInfo` objects, based on their implementation, the Visitor design
pattern was used. A public interface is exposed for this: `TimingInfoVisitor`. To use this
interface, someone should create a class that would look like this:
```java
class ImplementedActionVisitor implements TaskInfo.TimingInfoVisitor {
@Override
public void visit(TaskInfo.OneOffInfo oneOffInfo) { ... }
@Override
public void visit(TaskInfo.PeriodicInfo periodicInfo) { ... }
}
```
To use this visitor, someone would make the following calls:
```java
ImplementedActionVisitor visitor = new ImplementedActionVisitor();
myTimingInfo.accept(visitor);
```
## Loading Native parts
Some of the tasks running in the background require native parts of the browser
to be initialized. In order to simplify implementation of such tasks, we provide
a base `NativeBackgroundTask`
[implementation](https://cs.chromium.org/chromium/src/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/NativeBackgroundTask.java)
in the browser layer. It requires extending classes to implement 4 methods:
* `onStartTaskBeforeNativeLoaded(...)` where the background task can decide
whether conditions are correct to proceed with native initialization;
* `onStartTaskWithNative(...)` where the background task can be sure that
native initialization was completed, therefore it can depend on that part of
the browser;
* `onStopTaskBeforeNativeLoaded(...)` which is delivered to the background task
just like `onStopTask(...)` and the native parts of the browser are not
loaded;
* `onStopTaskWithNative(...)` which is delivered to the background task just
like `onStopTask(...)` and the native parts of the browser are loaded.
While in a normal execution, both `onStart...` methods are called, only one of
the stopping methods will be triggered, depending on whether the native parts of
the browser are loaded at the time the underlying scheduler decides to stop the
task.
## Launching Browser process
After the advent of servicfication in chrome, we have the option of launching a
background task in a reduced service manager only mode without the need to
launch the full browser process. In order to enable this, you have to override
`NativeBackgroundTask#supportsMinimalBrowser` and return true or false
depending on whether you want to launch service-manager only mode or full
browser.
## Background processing
Even though the `BackgroundTaskScheduler` provides functionality for invoking
code while the application is in the background, the `BackgroundTask` instance
is still invoked on the application's main thread.
This means that unless the operation is extremely quick, processing must happen
asynchronously, and the call to `onStartTask*(...)` must return before the task
has finished processing. In that case, the method should return once the
asychronous processing has begun, and invoke the `TaskFinishedCallback` when the
processing is finished, which typically happens on a different `Thread`,
`Handler`, or by using an `AsyncTask`.
If at any time the constraints given through the `TaskInfo` object do not hold
anymore, or if the system deems it necessary, `onStopTask*(...)` will be
invoked, requiring all activity to cease immediately. The task can return true
if the task needs to be rescheduled since it was canceled, or false otherwise.
Note that onStopTask*() is not invoked if the task itself invokes
`TaskFinishedCallback` or if the task is cancelled by the caller.
**The system will hold a wakelock from the time
`onStartTaskBeforeNativeLoaded(...)` is invoked until either the task itself
invokes the `TaskFinishedCallback`, or `onStopTask*(...)` is invoked.**
|