session-android/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Job.java

287 lines
8.1 KiB
Java

package org.thoughtcrime.securesms.jobmanager;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.session.libsession.messaging.utilities.Data;
import org.session.libsignal.utilities.Log;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* A durable unit of work.
*
* Jobs have {@link Parameters} that describe the conditions upon when you'd like them to run, how
* often they should be retried, and how long they should be retried for.
*
* Never rely on a specific instance of this class being run. It can be created and destroyed as the
* job is retried. State that you want to save is persisted to a {@link Data} object in
* {@link #serialize()}. Your job is then recreated using a {@link Factory} that you register in
* {@link JobManager.Configuration.Builder#setJobFactories(Map)}, which is given the saved
* {@link Data} bundle.
*
* @deprecated
* use <a href="https://developer.android.com/reference/androidx/work/WorkManager">WorkManager</a>
* API instead.
*/
public abstract class Job {
private static final String TAG = Log.tag(Job.class);
private final Parameters parameters;
private String id;
private int runAttempt;
private long nextRunAttemptTime;
protected Context context;
public Job(@NonNull Parameters parameters) {
this.parameters = parameters;
}
public final String getId() {
return id;
}
public final @NonNull Parameters getParameters() {
return parameters;
}
public final int getRunAttempt() {
return runAttempt;
}
public final long getNextRunAttemptTime() {
return nextRunAttemptTime;
}
/**
* This is already called by {@link JobController} during job submission, but if you ever run a
* job without submitting it to the {@link JobManager}, then you'll need to invoke this yourself.
*/
public final void setContext(@NonNull Context context) {
this.context = context;
}
/** Should only be invoked by {@link JobController} */
final void setId(@NonNull String id) {
this.id = id;
}
/** Should only be invoked by {@link JobController} */
final void setRunAttempt(int runAttempt) {
this.runAttempt = runAttempt;
}
/** Should only be invoked by {@link JobController} */
final void setNextRunAttemptTime(long nextRunAttemptTime) {
this.nextRunAttemptTime = nextRunAttemptTime;
}
@WorkerThread
final void onSubmit() {
Log.i(TAG, JobLogger.format(this, "onSubmit()"));
onAdded();
}
/**
* Called when the job is first submitted to the {@link JobManager}.
*/
@WorkerThread
public void onAdded() {
}
/**
* Called after a job has run and its determined that a retry is required.
*/
@WorkerThread
public void onRetry() {
}
/**
* Serialize your job state so that it can be recreated in the future.
*/
public abstract @NonNull Data serialize();
/**
* Returns the key that can be used to find the relevant factory needed to create your job.
*/
public abstract @NonNull String getFactoryKey();
/**
* Called to do your actual work.
*/
@WorkerThread
public abstract @NonNull Result run();
/**
* Called when your job has completely failed.
*/
@WorkerThread
public abstract void onCanceled();
public interface Factory<T extends Job> {
@NonNull T create(@NonNull Parameters parameters, @NonNull Data data);
}
public enum Result {
SUCCESS, FAILURE, RETRY
}
public static final class Parameters {
public static final int IMMORTAL = -1;
public static final int UNLIMITED = -1;
private final long createTime;
private final long lifespan;
private final int maxAttempts;
private final long maxBackoff;
private final int maxInstances;
private final String queue;
private final List<String> constraintKeys;
private Parameters(long createTime,
long lifespan,
int maxAttempts,
long maxBackoff,
int maxInstances,
@Nullable String queue,
@NonNull List<String> constraintKeys)
{
this.createTime = createTime;
this.lifespan = lifespan;
this.maxAttempts = maxAttempts;
this.maxBackoff = maxBackoff;
this.maxInstances = maxInstances;
this.queue = queue;
this.constraintKeys = constraintKeys;
}
public long getCreateTime() {
return createTime;
}
public long getLifespan() {
return lifespan;
}
public int getMaxAttempts() {
return maxAttempts;
}
public long getMaxBackoff() {
return maxBackoff;
}
public int getMaxInstances() {
return maxInstances;
}
public @Nullable String getQueue() {
return queue;
}
public List<String> getConstraintKeys() {
return constraintKeys;
}
public static final class Builder {
private long createTime = System.currentTimeMillis();
private long maxBackoff = TimeUnit.SECONDS.toMillis(30);
private long lifespan = IMMORTAL;
private int maxAttempts = 1;
private int maxInstances = UNLIMITED;
private String queue = null;
private List<String> constraintKeys = new LinkedList<>();
/** Should only be invoked by {@link JobController} */
Builder setCreateTime(long createTime) {
this.createTime = createTime;
return this;
}
/**
* Specify the amount of time this job is allowed to be retried. Defaults to {@link #IMMORTAL}.
*/
public @NonNull Builder setLifespan(long lifespan) {
this.lifespan = lifespan;
return this;
}
/**
* Specify the maximum number of times you want to attempt this job. Defaults to 1.
*/
public @NonNull Builder setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
return this;
}
/**
* Specify the longest amount of time to wait between retries. No guarantees that this will
* be respected on API >= 26.
*/
public @NonNull Builder setMaxBackoff(long maxBackoff) {
this.maxBackoff = maxBackoff;
return this;
}
/**
* Specify the maximum number of instances you'd want of this job at any given time. If
* enqueueing this job would put it over that limit, it will be ignored.
*
* Duplicates are determined by two jobs having the same {@link Job#getFactoryKey()}.
*
* This property is ignored if the job is submitted as part of a {@link JobManager.Chain}.
*
* Defaults to {@link #UNLIMITED}.
*/
public @NonNull Builder setMaxInstances(int maxInstances) {
this.maxInstances = maxInstances;
return this;
}
/**
* Specify a string representing a queue. All jobs within the same queue are run in a
* serialized fashion -- one after the other, in order of insertion. Failure of a job earlier
* in the queue has no impact on the execution of jobs later in the queue.
*/
public @NonNull Builder setQueue(@Nullable String queue) {
this.queue = queue;
return this;
}
/**
* Add a constraint via the key that was used to register its factory in
* {@link JobManager.Configuration)};
*/
public @NonNull Builder addConstraint(@NonNull String constraintKey) {
constraintKeys.add(constraintKey);
return this;
}
/**
* Set constraints via the key that was used to register its factory in
* {@link JobManager.Configuration)};
*/
public @NonNull Builder setConstraints(@NonNull List<String> constraintKeys) {
this.constraintKeys.clear();
this.constraintKeys.addAll(constraintKeys);
return this;
}
public @NonNull Parameters build() {
return new Parameters(createTime, lifespan, maxAttempts, maxBackoff, maxInstances, queue, constraintKeys);
}
}
}
}