JobHolder.java
package com.birbit.android.jobqueue;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.birbit.android.jobqueue.network.NetworkUtil;
import com.birbit.android.jobqueue.timer.Timer;
import java.util.Set;
/**
* Container class to address Jobs inside job manager.
*/
public class JobHolder {
/**
* Internal constant. Job's onRun method completed w/o any exception.
*/
public static final int RUN_RESULT_SUCCESS = 1;
/**
* Internal constant. Job's onRun method thrown an exception and either it does not want to
* run again or reached retry limit.
*/
public static final int RUN_RESULT_FAIL_RUN_LIMIT = 2;
/**
* Internal constant. Job's onRun method has thrown an exception and it was cancelled after it
* started.
*/
public static final int RUN_RESULT_FAIL_FOR_CANCEL = 3;
/**
* Internal constant. Job's onRun method failed but wants to retry.
*/
public static final int RUN_RESULT_TRY_AGAIN = 4;
/**
* The job decided not to run in shouldReRun method.
*/
public static final int RUN_RESULT_FAIL_SHOULD_RE_RUN = 5;
/**
* Internal constant. Job's onRun method has thrown an exception and another job with the
* same single instance id had been queued.
*/
public static final int RUN_RESULT_FAIL_SINGLE_ID = 6;
/**
* Internal constant. Used when job's onRun method has thrown an exception and it hit its
* cancel deadline.
*/
public static final int RUN_RESULT_HIT_DEADLINE = 7;
private Long insertionOrder;
public final String id;
public final boolean persistent;
private int priority;
public final String groupId;
private int runCount;
/**
* job will be delayed until this nanotime
*/
private long delayUntilNs;
/**
* When job is created, Timer.nanoTime() is assigned to {@code createdNs} value so that we know
* when job is created in relation to others
*/
private long createdNs;
private long runningSessionId;
/* package */ int requiredNetworkType;
/**
* When we should ignore the constraints
*/
private long deadlineNs;
/**
* What to do when deadline is reached
*/
private boolean cancelOnDeadline;
transient final Job job;
protected final Set<String> tags;
private volatile boolean cancelled;
private volatile boolean cancelledSingleId;
/**
* may be set after a job is run and cleared by the JobManager
*/
RetryConstraint retryConstraint;
/**
* Eventual exception thrown from the last execution of {@link Job#onRun}
*/
@Nullable private Throwable throwable;
/**
* @param id The ID of the Job
* @param persistent Is the job persistent
* @param priority Higher is better
* @param groupId which group does this job belong to? default null
* @param runCount Incremented each time job is fetched to run, initial value should be 0
* @param job Actual job to run
* @param createdNs System.nanotime
* @param delayUntilNs System.nanotime value: when job can be run the very first time
* @param runningSessionId The running session id for the job
* @param tags The tags of the Job
* @param requiredNetworkType The minimum type of network that is required to run this job
* @param deadlineNs System.nanotime value: when the job will ignore its constraints
* @param cancelOnDeadline true if job should be cancelled when deadline is reached, false otherwise
*/
private JobHolder(String id, boolean persistent, int priority, String groupId, int runCount, Job job, long createdNs,
long delayUntilNs, long runningSessionId, Set<String> tags,
int requiredNetworkType, long deadlineNs, boolean cancelOnDeadline) {
this.id = id;
this.persistent = persistent;
this.priority = priority;
this.groupId = groupId;
this.runCount = runCount;
this.createdNs = createdNs;
this.delayUntilNs = delayUntilNs;
this.job = job;
this.runningSessionId = runningSessionId;
this.requiredNetworkType = requiredNetworkType;
this.tags = tags;
this.deadlineNs = deadlineNs;
this.cancelOnDeadline = cancelOnDeadline;
}
/**
* runs the job w/o throwing any exceptions
* @param currentRunCount The current run count of the job
*
* @return RUN_RESULT
*/
int safeRun(int currentRunCount, Timer timer) {
return job.safeRun(this, currentRunCount, timer);
}
@NonNull public String getId() {
return id;
}
public final String getSingleInstanceId() {
if (tags != null) {
for (String tag : tags) {
if (tag.startsWith(Job.SINGLE_ID_TAG_PREFIX)) {
return tag;
}
}
}
return null;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
this.job.priority = this.priority;
}
public Long getInsertionOrder() {
return insertionOrder;
}
public void setInsertionOrder(long insertionOrder) {
this.insertionOrder = insertionOrder;
}
public void setDelayUntilNs(long delayUntilNs) {
this.delayUntilNs = delayUntilNs;
}
public int getRunCount() {
return runCount;
}
public void setRunCount(int runCount) {
this.runCount = runCount;
}
public long getCreatedNs() {
return createdNs;
}
public long getRunningSessionId() {
return runningSessionId;
}
public void setRunningSessionId(long runningSessionId) {
this.runningSessionId = runningSessionId;
}
public long getDeadlineNs() {
return deadlineNs;
}
public boolean shouldCancelOnDeadline() {
return cancelOnDeadline;
}
public long getDelayUntilNs() {
return delayUntilNs;
}
public Job getJob() {
return job;
}
public String getGroupId() {
return groupId;
}
public Set<String> getTags() {
return tags;
}
public void markAsCancelled() {
cancelled = true;
job.cancelled = true;
}
public boolean isCancelled() {
return cancelled;
}
public void markAsCancelledSingleId() {
cancelledSingleId = true;
markAsCancelled();
}
public boolean isCancelledSingleId() {
return cancelledSingleId;
}
@Override
public int hashCode() {
//we don't really care about overflow.
return id.hashCode();
}
@Override
public boolean equals(Object o) {
if(!(o instanceof JobHolder)) {
return false;
}
JobHolder other = (JobHolder) o;
return id.equals(other.id);
}
public boolean hasTags() {
return tags != null && tags.size() > 0;
}
public void setApplicationContext(Context applicationContext) {
this.job.setApplicationContext(applicationContext);
}
public void setDeadlineIsReached(boolean didReachDeadline) {
this.job.setDeadlineReached(didReachDeadline);
}
public boolean hasDeadline() {
return deadlineNs != Params.FOREVER;
}
public boolean hasDelay() {
return delayUntilNs != JobManager.NOT_DELAYED_JOB_DELAY;
}
public void onCancel(@CancelReason int cancelReason) {
job.onCancel(cancelReason, throwable);
}
public RetryConstraint getRetryConstraint() {
return retryConstraint;
}
void setThrowable(@Nullable Throwable throwable) {
this.throwable = throwable;
}
@Nullable
Throwable getThrowable() {
return throwable;
}
/**
* Returns the type of network required by this job.
* <p>
* Note that these network status can be compared to eachother and higher network type is a
* larger requirement. For instance, if this method returns
* {@link com.birbit.android.jobqueue.network.NetworkUtil.NetworkStatus#DISCONNECTED}, that does
* not mean job requires no network to run. Instead, it means it does not require any network
* to run.
*
* @return The minimum type of network connection that is required to run this job.
*/
@NetworkUtil.NetworkStatus
public int getRequiredNetworkType() {
return requiredNetworkType;
}
public static class Builder {
private int priority;
private static final int FLAG_PRIORITY = 1;
private String id;
private static final int FLAG_PERSISTENT = FLAG_PRIORITY << 1;
private boolean persistent;
private static final int FLAG_ID = FLAG_PERSISTENT << 1;
private String groupId;
private static final int FLAG_GROUP_ID = FLAG_ID << 1;
private int runCount = 0;
private Job job;
private static final int FLAG_JOB = FLAG_GROUP_ID << 1;
private long createdNs;
private static final int FLAG_CREATED_NS = FLAG_JOB << 1;
private long delayUntilNs = JobManager.NOT_DELAYED_JOB_DELAY;
private static final int FLAG_DELAY_UNTIL = FLAG_CREATED_NS << 1;
private Long insertionOrder;
private long runningSessionId;
private long deadlineNs = Params.FOREVER;
private boolean cancelOnDeadline = false;
private static final int FLAG_DEADLINE = FLAG_DELAY_UNTIL << 1;
private static final int FLAG_RUNNING_SESSION_ID = FLAG_DEADLINE << 1;
private int providedFlags = 0;
private Set<String> tags;
private static final int FLAG_TAGS = FLAG_RUNNING_SESSION_ID << 1;
@NetworkUtil.NetworkStatus
private int requiredNetworkType;
private static final int FLAG_REQ_NETWORK = FLAG_TAGS << 1;
private static final int REQUIRED_FLAGS = (FLAG_REQ_NETWORK << 1) - 1;
public Builder priority(int priority) {
this.priority = priority;
providedFlags |= FLAG_PRIORITY;
return this;
}
public Builder groupId(String groupId) {
this.groupId = groupId;
providedFlags |= FLAG_GROUP_ID;
return this;
}
public Builder tags(Set<String> tags) {
this.tags = tags;
providedFlags |= FLAG_TAGS;
return this;
}
public Builder runCount(int runCount) {
this.runCount = runCount;
return this;
}
public Builder persistent(boolean persistent) {
this.persistent = persistent;
providedFlags |= FLAG_PERSISTENT;
return this;
}
public Builder job(Job job) {
this.job = job;
providedFlags |= FLAG_JOB;
return this;
}
public Builder id(String id) {
this.id = id;
providedFlags |= FLAG_ID;
return this;
}
public Builder requiredNetworkType(@NetworkUtil.NetworkStatus int requiredNetworkType) {
this.requiredNetworkType = requiredNetworkType;
providedFlags |= FLAG_REQ_NETWORK;
return this;
}
public Builder createdNs(long createdNs) {
this.createdNs = createdNs;
providedFlags |= FLAG_CREATED_NS;
return this;
}
public Builder delayUntilNs(long delayUntilNs) {
this.delayUntilNs = delayUntilNs;
providedFlags |= FLAG_DELAY_UNTIL;
return this;
}
public Builder insertionOrder(long insertionOrder) {
this.insertionOrder = insertionOrder;
return this;
}
public Builder runningSessionId(long runningSessionId) {
this.runningSessionId = runningSessionId;
providedFlags |= FLAG_RUNNING_SESSION_ID;
return this;
}
public Builder deadline(long deadlineNs, boolean cancelOnDeadline) {
this.deadlineNs = deadlineNs;
this.cancelOnDeadline = cancelOnDeadline;
providedFlags |= FLAG_DEADLINE;
return this;
}
public JobHolder build() {
if (job == null) {
throw new IllegalArgumentException("must provide a job");
}
int flagCheck = REQUIRED_FLAGS & providedFlags;
if (flagCheck != REQUIRED_FLAGS) {
throw new IllegalArgumentException("must provide all required fields. your result:" + Long.toBinaryString(flagCheck));
}
JobHolder jobHolder = new JobHolder(id, persistent, priority, groupId, runCount, job, createdNs,
delayUntilNs, runningSessionId, tags, requiredNetworkType, deadlineNs, cancelOnDeadline);
if (insertionOrder != null) {
jobHolder.setInsertionOrder(insertionOrder);
}
job.updateFromJobHolder(jobHolder);
return jobHolder;
}
}
}