blob: f54f2f487b02ef6d5f5edcc5f96d58c633f1852e [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.work;
import android.annotation.SuppressLint;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.work.impl.model.WorkSpec;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* The base class for specifying parameters for work that should be enqueued in {@link WorkManager}.
* There are two concrete implementations of this class: {@link OneTimeWorkRequest} and
* {@link PeriodicWorkRequest}.
*/
public abstract class WorkRequest {
/**
* The default initial backoff time (in milliseconds) for work that has to be retried.
*/
public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L;
/**
* The maximum backoff time (in milliseconds) for work that has to be retried.
*/
@SuppressLint("MinMaxConstant")
public static final long MAX_BACKOFF_MILLIS = 5 * 60 * 60 * 1000; // 5 hours.
/**
* The minimum backoff time for work (in milliseconds) that has to be retried.
*/
@SuppressLint("MinMaxConstant")
public static final long MIN_BACKOFF_MILLIS = 10 * 1000; // 10 seconds.
private @NonNull UUID mId;
private @NonNull WorkSpec mWorkSpec;
private @NonNull Set<String> mTags;
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
protected WorkRequest(@NonNull UUID id, @NonNull WorkSpec workSpec, @NonNull Set<String> tags) {
mId = id;
mWorkSpec = workSpec;
mTags = tags;
}
/**
* Gets the unique identifier associated with this unit of work.
*
* @return The identifier for this unit of work
*/
public @NonNull UUID getId() {
return mId;
}
/**
* Gets the string for the unique identifier associated with this unit of work.
*
* @return The string identifier for this unit of work
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull String getStringId() {
return mId.toString();
}
/**
* Gets the {@link WorkSpec} associated with this unit of work.
*
* @return The {@link WorkSpec} for this unit of work
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull WorkSpec getWorkSpec() {
return mWorkSpec;
}
/**
* Gets the tags associated with this unit of work.
*
* @return The tags for this unit of work
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull Set<String> getTags() {
return mTags;
}
/**
* A builder for {@link WorkRequest}s. There are two concrete implementations of this class:
* {@link OneTimeWorkRequest.Builder} and {@link PeriodicWorkRequest.Builder}.
*
* @param <B> The concrete implementation of this Builder
* @param <W> The type of work object built by this Builder
*/
public abstract static class Builder<B extends Builder<?, ?>, W extends WorkRequest> {
boolean mBackoffCriteriaSet = false;
UUID mId;
WorkSpec mWorkSpec;
Set<String> mTags = new HashSet<>();
Class<? extends ListenableWorker> mWorkerClass;
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
/**
* Sets the backoff policy and backoff delay for the work. The default values are
* {@link BackoffPolicy#EXPONENTIAL} and
* {@value WorkRequest#DEFAULT_BACKOFF_DELAY_MILLIS}, respectively. {@code backoffDelay}
* will be clamped between {@link WorkRequest#MIN_BACKOFF_MILLIS} and
* {@link WorkRequest#MAX_BACKOFF_MILLIS}.
*
* @param backoffPolicy The {@link BackoffPolicy} to use when increasing backoff time
* @param backoffDelay Time to wait before retrying the work in {@code timeUnit} units
* @param timeUnit The {@link TimeUnit} for {@code backoffDelay}
* @return The current {@link Builder}
*/
public final @NonNull B setBackoffCriteria(
@NonNull BackoffPolicy backoffPolicy,
long backoffDelay,
@NonNull TimeUnit timeUnit) {
mBackoffCriteriaSet = true;
mWorkSpec.backoffPolicy = backoffPolicy;
mWorkSpec.setBackoffDelayDuration(timeUnit.toMillis(backoffDelay));
return getThis();
}
/**
* Sets the backoff policy and backoff delay for the work. The default values are
* {@link BackoffPolicy#EXPONENTIAL} and
* {@value WorkRequest#DEFAULT_BACKOFF_DELAY_MILLIS}, respectively. {@code duration} will
* be clamped between {@link WorkRequest#MIN_BACKOFF_MILLIS} and
* {@link WorkRequest#MAX_BACKOFF_MILLIS}.
*
* @param backoffPolicy The {@link BackoffPolicy} to use when increasing backoff time
* @param duration Time to wait before retrying the work
* @return The current {@link Builder}
*/
@RequiresApi(26)
public final @NonNull B setBackoffCriteria(
@NonNull BackoffPolicy backoffPolicy,
@NonNull Duration duration) {
mBackoffCriteriaSet = true;
mWorkSpec.backoffPolicy = backoffPolicy;
mWorkSpec.setBackoffDelayDuration(duration.toMillis());
return getThis();
}
/**
* Adds constraints to the {@link WorkRequest}.
*
* @param constraints The constraints for the work
* @return The current {@link Builder}
*/
public final @NonNull B setConstraints(@NonNull Constraints constraints) {
mWorkSpec.constraints = constraints;
return getThis();
}
/**
* Adds input {@link Data} to the work. If a worker has prerequisites in its chain, this
* Data will be merged with the outputs of the prerequisites using an {@link InputMerger}.
*
* @param inputData key/value pairs that will be provided to the worker
* @return The current {@link Builder}
*/
public final @NonNull B setInputData(@NonNull Data inputData) {
mWorkSpec.input = inputData;
return getThis();
}
/**
* Adds a tag for the work. You can query and cancel work by tags. Tags are particularly
* useful for modules or libraries to find and operate on their own work.
*
* @param tag A tag for identifying the work in queries.
* @return The current {@link Builder}
*/
public final @NonNull B addTag(@NonNull String tag) {
mTags.add(tag);
return getThis();
}
/**
* Specifies that the results of this work should be kept for at least the specified amount
* of time. After this time has elapsed, the results <b>may</b> be pruned at the discretion
* of WorkManager when there are no pending dependent jobs.
* <p>
* When the results of a work are pruned, it becomes impossible to query for its
* {@link WorkInfo}.
* <p>
* Specifying a long duration here may adversely affect performance in terms of app storage
* and database query time.
*
* @param duration The minimum duration of time (in {@code timeUnit} units) to keep the
* results of this work
* @param timeUnit The unit of time for {@code duration}
* @return The current {@link Builder}
*/
public final @NonNull B keepResultsForAtLeast(long duration, @NonNull TimeUnit timeUnit) {
mWorkSpec.minimumRetentionDuration = timeUnit.toMillis(duration);
return getThis();
}
/**
* Specifies that the results of this work should be kept for at least the specified amount
* of time. After this time has elapsed, the results <p>may</p> be pruned at the discretion
* of WorkManager when this WorkRequest has reached a finished state (see
* {@link WorkInfo.State#isFinished()}) and there are no pending dependent jobs.
* <p>
* When the results of a work are pruned, it becomes impossible to query for its
* {@link WorkInfo}.
* <p>
* Specifying a long duration here may adversely affect performance in terms of app storage
* and database query time.
*
* @param duration The minimum duration of time to keep the results of this work
* @return The current {@link Builder}
*/
@RequiresApi(26)
public final @NonNull B keepResultsForAtLeast(@NonNull Duration duration) {
mWorkSpec.minimumRetentionDuration = duration.toMillis();
return getThis();
}
/**
* Sets an initial delay for the {@link WorkRequest}.
*
* @param duration The length of the delay in {@code timeUnit} units
* @param timeUnit The units of time for {@code duration}
* @return The current {@link Builder}
* @throws IllegalArgumentException if the given initial delay will push the execution time
* past {@code Long.MAX_VALUE} and cause an overflow
*/
public @NonNull B setInitialDelay(long duration, @NonNull TimeUnit timeUnit) {
mWorkSpec.initialDelay = timeUnit.toMillis(duration);
if (Long.MAX_VALUE - System.currentTimeMillis() <= mWorkSpec.initialDelay) {
throw new IllegalArgumentException("The given initial delay is too large and will"
+ " cause an overflow!");
}
return getThis();
}
/**
* Sets an initial delay for the {@link WorkRequest}.
*
* @param duration The length of the delay
* @return The current {@link Builder} *
* @throws IllegalArgumentException if the given initial delay will push the execution time
* past {@code Long.MAX_VALUE} and cause an overflow
*/
@RequiresApi(26)
public @NonNull B setInitialDelay(@NonNull Duration duration) {
mWorkSpec.initialDelay = duration.toMillis();
if (Long.MAX_VALUE - System.currentTimeMillis() <= mWorkSpec.initialDelay) {
throw new IllegalArgumentException("The given initial delay is too large and will"
+ " cause an overflow!");
}
return getThis();
}
/**
* Marks the {@link WorkRequest} as important to the user. In this case, WorkManager
* provides an additional signal to the OS that this work is important.
*
* @param policy The {@link OutOfQuotaPolicy} to be used.
*/
@SuppressLint("MissingGetterMatchingBuilder")
public @NonNull B setExpedited(@NonNull OutOfQuotaPolicy policy) {
mWorkSpec.expedited = true;
mWorkSpec.outOfQuotaPolicy = policy;
return getThis();
}
/**
* Builds a {@link WorkRequest} based on this {@link Builder}.
*
* @return A {@link WorkRequest} based on this {@link Builder}
*/
public final @NonNull W build() {
W returnValue = buildInternal();
Constraints constraints = mWorkSpec.constraints;
// Check for unsupported constraints.
boolean hasUnsupportedConstraints =
(Build.VERSION.SDK_INT >= 24 && constraints.hasContentUriTriggers())
|| constraints.requiresBatteryNotLow()
|| constraints.requiresCharging()
|| (Build.VERSION.SDK_INT >= 23 && constraints.requiresDeviceIdle());
if (mWorkSpec.expedited && hasUnsupportedConstraints) {
throw new IllegalArgumentException(
"Expedited jobs only support network and storage constraints");
}
// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mWorkSpec);
mWorkSpec.id = mId.toString();
return returnValue;
}
abstract @NonNull W buildInternal();
abstract @NonNull B getThis();
/**
* Sets the initial state for this work. Used in testing only.
*
* @param state The {@link WorkInfo.State} to set
* @return The current {@link Builder}
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@VisibleForTesting
public final @NonNull B setInitialState(@NonNull WorkInfo.State state) {
mWorkSpec.state = state;
return getThis();
}
/**
* Sets the initial run attempt count for this work. Used in testing only.
*
* @param runAttemptCount The initial run attempt count
* @return The current {@link Builder}
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@VisibleForTesting
public final @NonNull B setInitialRunAttemptCount(int runAttemptCount) {
mWorkSpec.runAttemptCount = runAttemptCount;
return getThis();
}
/**
* Sets the period start time for this work. Used in testing only.
*
* @param periodStartTime the period start time in {@code timeUnit} units
* @param timeUnit The {@link TimeUnit} for {@code periodStartTime}
* @return The current {@link Builder}
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@VisibleForTesting
public final @NonNull B setPeriodStartTime(
long periodStartTime,
@NonNull TimeUnit timeUnit) {
mWorkSpec.periodStartTime = timeUnit.toMillis(periodStartTime);
return getThis();
}
/**
* Sets when the scheduler actually schedules the worker.
*
* @param scheduleRequestedAt The time at which the scheduler scheduled a worker.
* @param timeUnit The {@link TimeUnit} for {@code scheduleRequestedAt}
* @return The current {@link Builder}
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@VisibleForTesting
public final @NonNull B setScheduleRequestedAt(
long scheduleRequestedAt,
@NonNull TimeUnit timeUnit) {
mWorkSpec.scheduleRequestedAt = timeUnit.toMillis(scheduleRequestedAt);
return getThis();
}
}
}