mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Added README
// FREEBIE
This commit is contained in:
parent
35821d444e
commit
3d1007d101
238
jobqueue/README.md
Normal file
238
jobqueue/README.md
Normal file
@ -0,0 +1,238 @@
|
||||
# JobManager
|
||||
|
||||
An Android library that facilitates scheduling persistent jobs which are executed when their
|
||||
prerequisites have been met. Similar to Path's android-priority-queue.
|
||||
|
||||
## The JobManager Way
|
||||
|
||||
Android apps often need to perform blocking operations. A messaging app might need to make REST
|
||||
API calls over a network, send SMS messages, download attachments, and interact with a database.
|
||||
|
||||
The standard Android way to do these things are with Services, AsyncTasks, or a dedicated Thread.
|
||||
However, some of an app's operations might need to wait until certain dependencies are available
|
||||
(such as a network connection), and some of the operations might need to be durable (complete even if the
|
||||
app restarts before they have a chance to run). Doing that standard Android way can result in
|
||||
a lot of retry logic, timers for monitoring dependencies, and one-off code for making operations
|
||||
durable.
|
||||
|
||||
By contrast, the JobManager way allows operations to be broken up into Jobs. A Job represents a
|
||||
unit of work to be done, the prerequisites that need to be met (such as network access) before the
|
||||
work can execute, and the characteristics of the job (such as durable persistence).
|
||||
|
||||
Applications construct a `JobManager` at initialization time:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
This constructs a new `JobManager` with 5 consumer threads dedicated to executing Jobs. A
|
||||
Job looks like this:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob() {
|
||||
super(JobParameters.newBuilder().create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public onAdded() {
|
||||
// Called after the Job has been added to the queue.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() {
|
||||
// Here's where we execute our work.
|
||||
Log.w("SampleJob", "Hello, world!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
// This would be called if the job had failed.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
// Called if onRun() had thrown an exception to determine whether
|
||||
// onRun() should be called again.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
``
|
||||
|
||||
A Job is scheduled simply by adding it to the JobManager:
|
||||
|
||||
```
|
||||
this.jobManager.add(new SampleJob());
|
||||
```
|
||||
|
||||
## Persistence
|
||||
|
||||
To create durable Jobs, the JobManager needs to be given an interface responsible for serializing
|
||||
and deserializing Job objects. A `JavaJobSerializer` is included with JobManager that uses Java
|
||||
Serialization, but you can specify your own serializer if you wish:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The Job itself simply needs to declare itself as durable when constructed:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob() {
|
||||
super(JobParameters.newBuilder()
|
||||
.withPersistence()
|
||||
.create());
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Persistent jobs that are enqueued will be serialized to disk to ensure that they run even if
|
||||
the App restarts first. A Job's onAdded() method is called after the commit to disk is complete.
|
||||
|
||||
## Requirements
|
||||
|
||||
A Job might have certain requirements that need to be met before it can run. A requirement is
|
||||
represented by the `Requirement` class. Each `Requirement` must also have a corresponding
|
||||
`RequirementProvider` that is registered with the JobManager.
|
||||
|
||||
A `Requirement` tells you whether it is present when queried, while a `RequirementProvider`
|
||||
broadcasts to a listener when a Requirement's status might have changed. `Requirement` is attached
|
||||
to Job, while `RequirementProvider` is attached to JobManager.
|
||||
|
||||
|
||||
One common `Requirement` a `Job` might depend on is the presence of network connectivity.
|
||||
A `NetworkRequirement` is bundled with JobManager:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withRequirementProviders(new NetworkRequirementProvider(this))
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The Job itself simply needs to declare itself as having a `Requirement` when constructed:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob(Context context) {
|
||||
super(JobParameters.newBuilder()
|
||||
.withPersistence()
|
||||
.withRequirement(new NetworkRequirement(context))
|
||||
.create());
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
It is possible that Jobs (and Requirements) might require dependency injection. A simple example
|
||||
is `Context`, which many Jobs might require, but can't be persisted to disk for durable Jobs. Or
|
||||
maybe Jobs require more complex DI through libraries such as Dagger.
|
||||
|
||||
JobManager has an extremely primitive DI mechanism strictly for injecting `Context` objects into
|
||||
Jobs and Requirements after they're deserialized, and includes support for plugging in more complex
|
||||
DI systems such as Dagger.
|
||||
|
||||
The JobManager `Context` injection works by having your `Job` and/or `Requirement` implement the
|
||||
`ContextDependent` interface. `Job`s and `Requirement`s implementing that interface will get a
|
||||
`setContext(Context context)` call immediately after the persistent `Job` or `Requirement` is
|
||||
deserialized.
|
||||
|
||||
To plugin a more complex DI mechanism, simply pass an instance of the `DependencyInjector` interface
|
||||
to the `JobManager`:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application implements DependencyInjector {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withRequirementProviders(new NetworkRequirementProvider(this))
|
||||
.withDependencyInjector(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectDependencies(Object object) {
|
||||
// And here we do our DI magic.
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
`injectDependencies(Object object)` will be called for a `Job` before the job's `onAdded()` method
|
||||
is called, or after a persistent job is deserialized.
|
Loading…
Reference in New Issue
Block a user