| /* |
| * 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.content.Context; |
| import android.net.Network; |
| import android.net.Uri; |
| |
| import androidx.annotation.IntRange; |
| import androidx.annotation.Keep; |
| import androidx.annotation.MainThread; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.RequiresApi; |
| import androidx.annotation.RestrictTo; |
| import androidx.work.impl.utils.taskexecutor.TaskExecutor; |
| |
| import com.google.common.util.concurrent.ListenableFuture; |
| |
| import java.util.List; |
| import java.util.Set; |
| import java.util.UUID; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * A class that can perform work asynchronously in {@link WorkManager}. For most cases, we |
| * recommend using {@link Worker}, which offers a simple synchronous API that is executed on a |
| * pre-specified background thread. |
| * <p> |
| * ListenableWorker classes are instantiated at runtime by the {@link WorkerFactory} specified in |
| * the {@link Configuration}. The {@link #startWork()} method is called on the main thread. |
| * <p> |
| * In case the work is preempted and later restarted for any reason, a new instance of |
| * ListenableWorker is created. This means that {@code startWork} is called exactly once per |
| * ListenableWorker instance. A new ListenableWorker is created if a unit of work needs to be |
| * rerun. |
| * <p> |
| * A ListenableWorker is given a maximum of ten minutes to finish its execution and return a |
| * {@link Result}. After this time has expired, the worker will be signalled to stop and its |
| * {@link ListenableFuture} will be cancelled. |
| * <p> |
| * Exercise caution when <a href="WorkManager.html#worker_class_names">renaming or removing |
| * ListenableWorkers</a> from your codebase. |
| */ |
| |
| public abstract class ListenableWorker { |
| |
| private @NonNull Context mAppContext; |
| private @NonNull WorkerParameters mWorkerParams; |
| |
| private volatile boolean mStopped; |
| |
| private boolean mUsed; |
| |
| /** |
| * @param appContext The application {@link Context} |
| * @param workerParams Parameters to setup the internal state of this worker |
| */ |
| @Keep |
| @SuppressLint("BanKeepAnnotation") |
| public ListenableWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) { |
| // Actually make sure we don't get nulls. |
| if (appContext == null) { |
| throw new IllegalArgumentException("Application Context is null"); |
| } |
| |
| if (workerParams == null) { |
| throw new IllegalArgumentException("WorkerParameters is null"); |
| } |
| |
| mAppContext = appContext; |
| mWorkerParams = workerParams; |
| } |
| |
| /** |
| * Gets the application {@link android.content.Context}. |
| * |
| * @return The application {@link android.content.Context} |
| */ |
| public final @NonNull Context getApplicationContext() { |
| return mAppContext; |
| } |
| |
| /** |
| * Gets the ID of the {@link WorkRequest} that created this Worker. |
| * |
| * @return The ID of the creating {@link WorkRequest} |
| */ |
| public final @NonNull UUID getId() { |
| return mWorkerParams.getId(); |
| } |
| |
| /** |
| * Gets the input data. Note that in the case that there are multiple prerequisites for this |
| * Worker, the input data has been run through an {@link InputMerger}. |
| * |
| * @return The input data for this work |
| * @see OneTimeWorkRequest.Builder#setInputMerger(Class) |
| */ |
| public final @NonNull Data getInputData() { |
| return mWorkerParams.getInputData(); |
| } |
| |
| /** |
| * Gets a {@link java.util.Set} of tags associated with this Worker's {@link WorkRequest}. |
| * |
| * @return The {@link java.util.Set} of tags associated with this Worker's {@link WorkRequest} |
| * @see WorkRequest.Builder#addTag(String) |
| */ |
| public final @NonNull Set<String> getTags() { |
| return mWorkerParams.getTags(); |
| } |
| |
| /** |
| * Gets the list of content {@link android.net.Uri}s that caused this Worker to execute. See |
| * {@code JobParameters#getTriggeredContentUris()} for relevant {@code JobScheduler} code. |
| * |
| * @return The list of content {@link android.net.Uri}s that caused this Worker to execute |
| * @see Constraints.Builder#addContentUriTrigger(android.net.Uri, boolean) |
| */ |
| @RequiresApi(24) |
| public final @NonNull List<Uri> getTriggeredContentUris() { |
| return mWorkerParams.getTriggeredContentUris(); |
| } |
| |
| /** |
| * Gets the list of content authorities that caused this Worker to execute. See |
| * {@code JobParameters#getTriggeredContentAuthorities()} for relevant {@code JobScheduler} |
| * code. |
| * |
| * @return The list of content authorities that caused this Worker to execute |
| */ |
| @RequiresApi(24) |
| public final @NonNull List<String> getTriggeredContentAuthorities() { |
| return mWorkerParams.getTriggeredContentAuthorities(); |
| } |
| |
| /** |
| * Gets the {@link android.net.Network} to use for this Worker. This method returns |
| * {@code null} if there is no network needed for this work request. |
| * |
| * @return The {@link android.net.Network} specified by the OS to be used with this Worker |
| */ |
| @RequiresApi(28) |
| public final @Nullable Network getNetwork() { |
| return mWorkerParams.getNetwork(); |
| } |
| |
| /** |
| * Gets the current run attempt count for this work. Note that for periodic work, this value |
| * gets reset between periods. |
| * |
| * @return The current run attempt count for this work. |
| */ |
| @IntRange(from = 0) |
| public final int getRunAttemptCount() { |
| return mWorkerParams.getRunAttemptCount(); |
| } |
| |
| /** |
| * Override this method to start your actual background processing. This method is called on |
| * the main thread. |
| * <p> |
| * A ListenableWorker is given a maximum of ten minutes to finish its execution and return a |
| * {@link Result}. After this time has expired, the worker will be signalled to stop and its |
| * {@link ListenableFuture} will be cancelled. |
| * |
| * @return A {@link ListenableFuture} with the {@link Result} of the computation. If you |
| * cancel this Future, WorkManager will treat this unit of work as failed. |
| */ |
| @MainThread |
| public abstract @NonNull ListenableFuture<Result> startWork(); |
| |
| /** |
| * Updates {@link ListenableWorker} progress. |
| * |
| * @param data The progress {@link Data} |
| * @return A {@link ListenableFuture} which resolves after progress is persisted. |
| * Cancelling this future is a no-op. |
| */ |
| @NonNull |
| public final ListenableFuture<Void> setProgressAsync(@NonNull Data data) { |
| return mWorkerParams.getProgressUpdater() |
| .updateProgress(getApplicationContext(), getId(), data); |
| } |
| |
| /** |
| * Returns {@code true} if this Worker has been told to stop. This could be because of an |
| * explicit cancellation signal by the user, or because the system has decided to preempt the |
| * task. In these cases, the results of the work will be ignored by WorkManager and it is safe |
| * to stop the computation. WorkManager will retry the work at a later time if necessary. |
| * |
| * @return {@code true} if the work operation has been interrupted |
| */ |
| public final boolean isStopped() { |
| return mStopped; |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public final void stop() { |
| mStopped = true; |
| onStopped(); |
| } |
| |
| /** |
| * This method is invoked when this Worker has been told to stop. This could happen due |
| * to an explicit cancellation signal by the user, or because the system has decided to preempt |
| * the task. In these cases, the results of the work will be ignored by WorkManager. All |
| * processing in this method should be lightweight - there are no contractual guarantees about |
| * which thread will invoke this call, so this should not be a long-running or blocking |
| * operation. |
| */ |
| public void onStopped() { |
| // Do nothing by default. |
| } |
| |
| /** |
| * @return {@code true} if this worker has already been marked as used |
| * @see #setUsed() |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public final boolean isUsed() { |
| return mUsed; |
| } |
| |
| /** |
| * Marks this worker as used to make sure we enforce the policy that workers can only be used |
| * once and that WorkerFactories return a new instance each time. |
| * @see #isUsed() |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public final void setUsed() { |
| mUsed = true; |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public @NonNull Executor getBackgroundExecutor() { |
| return mWorkerParams.getBackgroundExecutor(); |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public @NonNull TaskExecutor getTaskExecutor() { |
| return mWorkerParams.getTaskExecutor(); |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public @NonNull WorkerFactory getWorkerFactory() { |
| return mWorkerParams.getWorkerFactory(); |
| } |
| |
| /** |
| * The result of a {@link ListenableWorker}'s computation. Call {@link #success()}, |
| * {@link #failure()}, or {@link #retry()} or one of their variants to generate an object |
| * indicating what happened in your background work. |
| */ |
| public abstract static class Result { |
| /** |
| * Returns an instance of {@link Result} that can be used to indicate that the work |
| * completed successfully. Any work that depends on this can be executed as long as all of |
| * its other dependencies and constraints are met. |
| * |
| * @return An instance of {@link Result} indicating successful execution of work |
| */ |
| @NonNull |
| public static Result success() { |
| return new Success(); |
| } |
| |
| /** |
| * Returns an instance of {@link Result} that can be used to indicate that the work |
| * completed successfully. Any work that depends on this can be executed as long as all of |
| * its other dependencies and constraints are met. |
| * |
| * @param outputData A {@link Data} object that will be merged into the input Data of any |
| * OneTimeWorkRequest that is dependent on this work |
| * @return An instance of {@link Result} indicating successful execution of work |
| */ |
| @NonNull |
| public static Result success(@NonNull Data outputData) { |
| return new Success(outputData); |
| } |
| |
| /** |
| * Returns an instance of {@link Result} that can be used to indicate that the work |
| * encountered a transient failure and should be retried with backoff specified in |
| * {@link WorkRequest.Builder#setBackoffCriteria(BackoffPolicy, long, TimeUnit)}. |
| * |
| * @return An instance of {@link Result} indicating that the work needs to be retried |
| */ |
| @NonNull |
| public static Result retry() { |
| return new Retry(); |
| } |
| |
| /** |
| * Returns an instance of {@link Result} that can be used to indicate that the work |
| * completed with a permanent failure. Any work that depends on this will also be marked as |
| * failed and will not be run. <b>If you need child workers to run, you need to use |
| * {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage |
| * of the chain of work. |
| * |
| * @return An instance of {@link Result} indicating failure when executing work |
| */ |
| @NonNull |
| public static Result failure() { |
| return new Failure(); |
| } |
| |
| /** |
| * Returns an instance of {@link Result} that can be used to indicate that the work |
| * completed with a permanent failure. Any work that depends on this will also be marked as |
| * failed and will not be run. <b>If you need child workers to run, you need to use |
| * {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage |
| * of the chain of work. |
| * |
| * @param outputData A {@link Data} object that can be used to keep track of why the work |
| * failed |
| * @return An instance of {@link Result} indicating failure when executing work |
| */ |
| @NonNull |
| public static Result failure(@NonNull Data outputData) { |
| return new Failure(outputData); |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| Result() { |
| // Restricting access to the constructor, to give Result a sealed class |
| // like behavior. |
| } |
| |
| /** |
| * Used to indicate that the work completed successfully. Any work that depends on this |
| * can be executed as long as all of its other dependencies and constraints are met. |
| * |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public static final class Success extends Result { |
| private final Data mOutputData; |
| |
| public Success() { |
| this(Data.EMPTY); |
| } |
| |
| /** |
| * @param outputData A {@link Data} object that will be merged into the input Data of |
| * any OneTimeWorkRequest that is dependent on this work |
| */ |
| public Success(@NonNull Data outputData) { |
| super(); |
| mOutputData = outputData; |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public Data getOutputData() { |
| return mOutputData; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| Success success = (Success) o; |
| |
| return mOutputData.equals(success.mOutputData); |
| } |
| |
| @Override |
| public int hashCode() { |
| String name = Success.class.getName(); |
| return 31 * name.hashCode() + mOutputData.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return "Success {" + "mOutputData=" + mOutputData + '}'; |
| } |
| } |
| |
| /** |
| * Used to indicate that the work completed with a permanent failure. Any work that depends |
| * on this will also be marked as failed and will not be run. <b>If you need child workers |
| * to run, you need to return {@link Result.Success}</b>; failure indicates a permanent |
| * stoppage of the chain of work. |
| * |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public static final class Failure extends Result { |
| private final Data mOutputData; |
| |
| public Failure() { |
| this(Data.EMPTY); |
| } |
| |
| /** |
| * @param outputData A {@link Data} object that can be used to keep track of why the |
| * work failed |
| */ |
| public Failure(@NonNull Data outputData) { |
| super(); |
| mOutputData = outputData; |
| } |
| |
| /** |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public Data getOutputData() { |
| return mOutputData; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| Failure failure = (Failure) o; |
| |
| return mOutputData.equals(failure.mOutputData); |
| } |
| |
| @Override |
| public int hashCode() { |
| String name = Failure.class.getName(); |
| return 31 * name.hashCode() + mOutputData.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return "Failure {" + "mOutputData=" + mOutputData + '}'; |
| } |
| } |
| |
| /** |
| * Used to indicate that the work encountered a transient failure and should be retried with |
| * backoff specified in |
| * {@link WorkRequest.Builder#setBackoffCriteria(BackoffPolicy, long, TimeUnit)}. |
| * |
| * @hide |
| */ |
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) |
| public static final class Retry extends Result { |
| public Retry() { |
| super(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| // We are treating all instances of Retry as equivalent. |
| return o != null && getClass() == o.getClass(); |
| } |
| |
| @Override |
| public int hashCode() { |
| String name = Retry.class.getName(); |
| return name.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return "Retry"; |
| } |
| } |
| } |
| } |