public abstract class

ListenableWorker

extends java.lang.Object

 java.lang.Object

↳androidx.work.ListenableWorker

Subclasses:

WorkManagerScheduler.SchedulerWorker, RxWorker, Worker, RemoteListenableWorker, DiagnosticsWorker, CombineContinuationsWorker, ConstraintTrackingWorker, RxWorker

Gradle dependencies

compile group: 'androidx.work', name: 'work-runtime', version: '2.8.0-alpha02'

  • groupId: androidx.work
  • artifactId: work-runtime
  • version: 2.8.0-alpha02

Artifact androidx.work:work-runtime:2.8.0-alpha02 it located at Google repository (https://maven.google.com/)

Overview

A class that can perform work asynchronously in WorkManager. For most cases, we recommend using Worker, which offers a simple synchronous API that is executed on a pre-specified background thread.

ListenableWorker classes are instantiated at runtime by the WorkerFactory specified in the Configuration. The ListenableWorker.startWork() method is called on the main thread.

In case the work is preempted and later restarted for any reason, a new instance of ListenableWorker is created. This means that startWork is called exactly once per ListenableWorker instance. A new ListenableWorker is created if a unit of work needs to be rerun.

A ListenableWorker is given a maximum of ten minutes to finish its execution and return a ListenableWorker.Result. After this time has expired, the worker will be signalled to stop and its will be cancelled.

Exercise caution when renaming or removing ListenableWorkers from your codebase.

Summary

Constructors
publicListenableWorker(Context appContext, WorkerParameters workerParams)

Methods
public final ContextgetApplicationContext()

Gets the application .

public java.util.concurrent.ExecutorgetBackgroundExecutor()

public <any>getForegroundInfoAsync()

Return an instance of ForegroundInfo if the WorkRequest is important to the user.

public final java.util.UUIDgetId()

Gets the ID of the WorkRequest that created this Worker.

public final DatagetInputData()

Gets the input data.

public final NetworkgetNetwork()

Gets the to use for this Worker.

public final intgetRunAttemptCount()

Gets the current run attempt count for this work.

public final java.util.Set<java.lang.String>getTags()

Gets a java.util.Set of tags associated with this Worker's WorkRequest.

public TaskExecutorgetTaskExecutor()

public final java.util.List<java.lang.String>getTriggeredContentAuthorities()

Gets the list of content authorities that caused this Worker to execute.

public final java.util.List<Uri>getTriggeredContentUris()

Gets the list of content s that caused this Worker to execute.

public WorkerFactorygetWorkerFactory()

public final booleanisStopped()

Returns true if this Worker has been told to stop.

public final booleanisUsed()

public voidonStopped()

This method is invoked when this Worker has been told to stop.

public final <any>setForegroundAsync(ForegroundInfo foregroundInfo)

This specifies that the WorkRequest is long-running or otherwise important.

public <any>setProgressAsync(Data data)

Updates ListenableWorker progress.

public final voidsetUsed()

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.

public abstract <any>startWork()

Override this method to start your actual background processing.

public final voidstop()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public ListenableWorker(Context appContext, WorkerParameters workerParams)

Parameters:

appContext: The application
workerParams: Parameters to setup the internal state of this worker

Methods

public final Context getApplicationContext()

Gets the application .

Returns:

The application

public final java.util.UUID getId()

Gets the ID of the WorkRequest that created this Worker.

Returns:

The ID of the creating WorkRequest

public final Data getInputData()

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 InputMerger.

Returns:

The input data for this work

See also: OneTimeWorkRequest.Builder.setInputMerger(Class)

public final java.util.Set<java.lang.String> getTags()

Gets a java.util.Set of tags associated with this Worker's WorkRequest.

Returns:

The java.util.Set of tags associated with this Worker's WorkRequest

See also: WorkRequest.Builder.addTag(String)

public final java.util.List<Uri> getTriggeredContentUris()

Gets the list of content s that caused this Worker to execute. See JobParameters#getTriggeredContentUris() for relevant JobScheduler code.

Returns:

The list of content s that caused this Worker to execute

See also: Constraints.Builder

public final java.util.List<java.lang.String> getTriggeredContentAuthorities()

Gets the list of content authorities that caused this Worker to execute. See JobParameters#getTriggeredContentAuthorities() for relevant JobScheduler code.

Returns:

The list of content authorities that caused this Worker to execute

public final Network getNetwork()

Gets the to use for this Worker. This method returns null if there is no network needed for this work request.

Returns:

The specified by the OS to be used with this Worker

public final int getRunAttemptCount()

Gets the current run attempt count for this work. Note that for periodic work, this value gets reset between periods.

Returns:

The current run attempt count for this work.

public abstract <any> startWork()

Override this method to start your actual background processing. This method is called on the main thread.

A ListenableWorker has a well defined execution window to to finish its execution and return a ListenableWorker.Result. After this time has expired, the worker will be signalled to stop and its will be cancelled.

The future will also be cancelled if this worker is stopped for any reason (see ListenableWorker.onStopped()).

Returns:

A ListenableFuture with the ListenableWorker.Result of the computation. If you cancel this Future, WorkManager will treat this unit of work as failed.

public <any> setProgressAsync(Data data)

Updates ListenableWorker progress.

Parameters:

data: The progress Data

Returns:

A which resolves after progress is persisted. Cancelling this future is a no-op.

public final <any> setForegroundAsync(ForegroundInfo foregroundInfo)

This specifies that the WorkRequest is long-running or otherwise important. In this case, WorkManager provides a signal to the OS that the process should be kept alive if possible while this work is executing.

Calls to setForegroundAsync *must* complete before a ListenableWorker signals completion by returning a ListenableWorker.Result.

Under the hood, WorkManager manages and runs a foreground service on your behalf to execute this WorkRequest, showing the notification provided in ForegroundInfo.

Calling setForegroundAsync will fail with an java.lang.IllegalStateException when the process is subject to foreground service restrictions. Consider using WorkRequest.Builder.setExpedited(OutOfQuotaPolicy) and ListenableWorker.getForegroundInfoAsync() instead.

Parameters:

foregroundInfo: The ForegroundInfo

Returns:

A which resolves after the ListenableWorker transitions to running in the context of a foreground .

public <any> getForegroundInfoAsync()

Return an instance of ForegroundInfo if the WorkRequest is important to the user. In this case, WorkManager provides a signal to the OS that the process should be kept alive while this work is executing.

Prior to Android S, WorkManager manages and runs a foreground service on your behalf to execute the WorkRequest, showing the notification provided in the ForegroundInfo. To update this notification subsequently, the application can use .

Starting in Android S and above, WorkManager manages this WorkRequest using an immediate job.

Returns:

A of ForegroundInfo instance if the WorkRequest is marked immediate. For more information look at WorkRequest.Builder.setExpedited(OutOfQuotaPolicy).

public final boolean isStopped()

Returns 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.

Returns:

true if the work operation has been interrupted

public final void stop()

public void onStopped()

This method is invoked when this Worker has been told to stop. At this point, the returned by the instance of ListenableWorker.startWork() is also cancelled. 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 final boolean isUsed()

Returns:

true if this worker has already been marked as used

See also: ListenableWorker.setUsed()

public final void setUsed()

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 also: ListenableWorker.isUsed()

public java.util.concurrent.Executor getBackgroundExecutor()

public TaskExecutor getTaskExecutor()

public WorkerFactory getWorkerFactory()

Source

/*
 * 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.content.Context;
import android.net.Network;
import android.net.Uri;

import androidx.annotation.IntRange;
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.futures.SettableFuture;
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
     */
    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 has a well defined
     * <a href="https://d.android.com/reference/android/app/job/JobScheduler">execution window</a>
     * to 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>
     * The future will also be cancelled if this worker is stopped for any reason
     * (see {@link #onStopped()}).
     *
     * @return A {@code 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 ListenableFuture<Void> setProgressAsync(@NonNull Data data) {
        return mWorkerParams.getProgressUpdater()
                .updateProgress(getApplicationContext(), getId(), data);
    }

    /**
     * This specifies that the {@link WorkRequest} is long-running or otherwise important.  In
     * this case, WorkManager provides a signal to the OS that the process should be kept alive
     * if possible while this work is executing.
     * <p>
     * Calls to {@code setForegroundAsync} *must* complete before a {@link ListenableWorker}
     * signals completion by returning a {@link Result}.
     * <p>
     * Under the hood, WorkManager manages and runs a foreground service on your behalf to
     * execute this WorkRequest, showing the notification provided in
     * {@link ForegroundInfo}.
     * <p>
     * Calling {@code setForegroundAsync} will fail with an
     * {@link IllegalStateException} when the process is subject to foreground
     * service restrictions. Consider using
     * {@link WorkRequest.Builder#setExpedited(OutOfQuotaPolicy)} and
     * {@link ListenableWorker#getForegroundInfoAsync()} instead.
     *
     * @param foregroundInfo The {@link ForegroundInfo}
     * @return A {@link ListenableFuture} which resolves after the {@link ListenableWorker}
     * transitions to running in the context of a foreground {@link android.app.Service}.
     */
    @NonNull
    public final ListenableFuture<Void> setForegroundAsync(@NonNull ForegroundInfo foregroundInfo) {
        return mWorkerParams.getForegroundUpdater()
                .setForegroundAsync(getApplicationContext(), getId(), foregroundInfo);
    }

    /**
     * Return an instance of {@link  ForegroundInfo} if the {@link WorkRequest} is important to
     * the user.  In this case, WorkManager provides a signal to the OS that the process should
     * be kept alive while this work is executing.
     * <p>
     * Prior to Android S, WorkManager manages and runs a foreground service on your behalf to
     * execute the WorkRequest, showing the notification provided in the {@link ForegroundInfo}.
     * To update this notification subsequently, the application can use
     * {@link android.app.NotificationManager}.
     * <p>
     * Starting in Android S and above, WorkManager manages this WorkRequest using an immediate job.
     *
     * @return A {@link ListenableFuture} of {@link ForegroundInfo} instance if the WorkRequest
     * is marked immediate. For more information look at
     * {@link WorkRequest.Builder#setExpedited(OutOfQuotaPolicy)}.
     */
    @NonNull
    public ListenableFuture<ForegroundInfo> getForegroundInfoAsync() {
        SettableFuture<ForegroundInfo> future = SettableFuture.create();
        String message =
                "Expedited WorkRequests require a ListenableWorker to provide an implementation for"
                        + " `getForegroundInfoAsync()`";
        future.setException(new IllegalStateException(message));
        return future;
    }

    /**
     * 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.  At this point, the
     * {@link ListenableFuture} returned by the instance of {@link #startWork()} is
     * also cancelled.  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);
        }

        /**
         * @return The output {@link Data} which will be merged into the input {@link Data} of
         * any {@link OneTimeWorkRequest} that is dependent on this work request.
         */
        @NonNull
        public abstract Data getOutputData();

        /**
         * @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;
            }

            @Override
            public @NonNull 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();
            }

            @NonNull
            @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;
            }

            @Override
            public @NonNull 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();
            }

            @NonNull
            @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();
            }

            @NonNull
            @Override
            public Data getOutputData() {
                return Data.EMPTY;
            }


            @NonNull
            @Override
            public String toString() {
                return "Retry";
            }
        }
    }
}