Gradle dependencies
compile group: 'androidx.work', name: 'work-runtime', version: '2.10.0-alpha03'
- groupId: androidx.work
- artifactId: work-runtime
- version: 2.10.0-alpha03
Artifact androidx.work:work-runtime:2.10.0-alpha03 it located at Google repository (https://maven.google.com/)
Overview
A concrete implementation of WorkManager.
Summary
Methods |
---|
public abstract WorkContinuation | beginUniqueWork(java.lang.String uniqueWorkName, ExistingWorkPolicy existingWorkPolicy, java.util.List<OneTimeWorkRequest> work)
This method allows you to begin unique chains of work for situations where you only want one
chain with a given name to be active at a time. |
public abstract WorkContinuation | beginWith(java.util.List<OneTimeWorkRequest> work)
Begins a chain with one or more OneTimeWorkRequests, which can be enqueued together
in the future using WorkContinuation.enqueue(). |
public abstract Operation | cancelAllWork()
Cancels all unfinished work. |
public abstract Operation | cancelAllWorkByTag(java.lang.String tag)
Cancels all unfinished work with the given tag. |
public abstract Operation | cancelUniqueWork(java.lang.String uniqueWorkName)
Cancels all unfinished work in the work chain with the given name. |
public abstract Operation | cancelWorkById(java.util.UUID id)
Cancels work with the given id if it isn't finished. |
public void | closeDatabase()
Cancels workmanager's scope and closes the database |
public abstract PendingIntent | createCancelPendingIntent(java.util.UUID id)
Creates a PendingIntent which can be used to cancel a WorkRequest with the
given id. |
public WorkContinuationImpl | createWorkContinuationForUniquePeriodicWork(java.lang.String uniqueWorkName, ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy, PeriodicWorkRequest periodicWork)
Creates a WorkContinuation for the given unique PeriodicWorkRequest. |
public abstract Operation | enqueue(java.util.List<WorkRequest> requests)
Enqueues one or more items for background processing. |
public abstract Operation | enqueueUniquePeriodicWork(java.lang.String uniqueWorkName, ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy, PeriodicWorkRequest periodicWork)
This method allows you to enqueue a uniquely-named PeriodicWorkRequest, where only
one PeriodicWorkRequest of a particular name can be active at a time. |
public abstract Operation | enqueueUniqueWork(java.lang.String uniqueWorkName, ExistingWorkPolicy existingWorkPolicy, java.util.List<OneTimeWorkRequest> work)
This method allows you to enqueue work requests to a uniquely-named
WorkContinuation, where only one continuation of a particular name can be active at
a time. |
public Context | getApplicationContext()
|
public Configuration | getConfiguration()
|
public static WorkManagerImpl | getInstance()
Retrieves the singleton instance of WorkManagerImpl. |
public static WorkManagerImpl | getInstance(Context context)
Retrieves the singleton instance of WorkManagerImpl. |
public abstract <any> | getLastCancelAllTimeMillis()
Gets a of the last time all work was cancelled. |
public abstract LiveData<java.lang.Long> | getLastCancelAllTimeMillisLiveData()
Gets a LiveData of the last time all work was cancelled. |
public PreferenceUtils | getPreferenceUtils()
|
public Processor | getProcessor()
|
public RemoteWorkManager | getRemoteWorkManager()
|
public java.util.List<Scheduler> | getSchedulers()
|
public Trackers | getTrackers()
|
public WorkDatabase | getWorkDatabase()
|
public abstract <any> | getWorkInfoById(java.util.UUID id)
Gets a of the WorkInfo for a given work id. |
public <any> | getWorkInfoByIdFlow(java.util.UUID id)
|
public abstract LiveData<WorkInfo> | getWorkInfoByIdLiveData(java.util.UUID id)
Gets a LiveData of the WorkInfo for a given work id. |
public abstract <any> | getWorkInfos(WorkQuery workQuery)
Gets the of the java.util.List of WorkInfo for all work
referenced by the WorkQuery specification. |
public abstract <any> | getWorkInfosByTag(java.lang.String tag)
Gets a of the WorkInfo for all work for a given tag. |
public <any> | getWorkInfosByTagFlow(java.lang.String tag)
|
public abstract LiveData<java.util.List> | getWorkInfosByTagLiveData(java.lang.String tag)
Gets a LiveData of the WorkInfo for all work for a given tag. |
public <any> | getWorkInfosFlow(WorkQuery workQuery)
|
public abstract <any> | getWorkInfosForUniqueWork(java.lang.String uniqueWorkName)
Gets a of the WorkInfo for all work in a work chain
with a given unique name. |
public <any> | getWorkInfosForUniqueWorkFlow(java.lang.String uniqueWorkName)
|
public abstract LiveData<java.util.List> | getWorkInfosForUniqueWorkLiveData(java.lang.String uniqueWorkName)
Gets a LiveData of the WorkInfo for all work in a work chain with a given
unique name. |
public abstract LiveData<java.util.List> | getWorkInfosLiveData(WorkQuery workQuery)
Gets the LiveData of the java.util.List of WorkInfo for all work
referenced by the WorkQuery specification. |
public TaskExecutor | getWorkTaskExecutor()
|
public static void | initialize(Context context, Configuration configuration)
Initializes the singleton instance of WorkManagerImpl. |
public static boolean | isInitialized()
|
public void | onForceStopRunnableCompleted()
A way for ForceStopRunnable to tell WorkManagerImpl that it has completed. |
public abstract Operation | pruneWork()
Prunes all eligible finished work from the internal database. |
public void | rescheduleEligibleWork()
Reschedules all the eligible work. |
public static void | setDelegate(WorkManagerImpl delegate)
|
public void | setReschedulePendingResult(BroadcastReceiver.PendingResult rescheduleReceiverResult)
This method is invoked by
RescheduleReceiver
after a call to BroadcastReceiver . |
public void | stopForegroundWork(WorkGenerationalId id)
|
public <any> | updateWork(WorkRequest request)
|
from WorkManager | beginUniqueWork, beginWith, enqueue, enqueueUniqueWork |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
MAX_PRE_JOB_SCHEDULER_API_LEVELpublic static final int
MIN_JOB_SCHEDULER_API_LEVELpublic static final int
CONTENT_URI_TRIGGER_API_LEVELpublic static final java.lang.String
REMOTE_WORK_MANAGER_CLIENTConstructors
Create an instance of WorkManagerImpl.
Parameters:
context: The application
configuration: The Configuration configuration
workTaskExecutor: The TaskExecutor for running "processing" jobs, such as
enqueueing, scheduling, cancellation, etc.
workDatabase: The WorkDatabase instance
processor: The Processor instance
trackers: Trackers
Methods
Parameters:
delegate: The delegate for WorkManagerImpl for testing; null to use the
default instance
Deprecated: Call WorkManagerImpl.getInstance(Context) instead.
Retrieves the singleton instance of WorkManagerImpl.
Returns:
The singleton instance of WorkManagerImpl
public static boolean
isInitialized()
Retrieves the singleton instance of WorkManagerImpl.
Parameters:
context: A context for on-demand initialization.
Returns:
The singleton instance of WorkManagerImpl
public static void
initialize(Context context,
Configuration configuration)
Initializes the singleton instance of WorkManagerImpl. You should only do this if
you want to use a custom Configuration object and have disabled
WorkManagerInitializer.
Parameters:
context: A object for configuration purposes. Internally, this
class will call , so you may
safely pass in any Context without risking a memory leak.
configuration: The Configuration for used to set up WorkManager.
public Context
getApplicationContext()
Returns:
The application associated with this WorkManager.
Returns:
The WorkDatabase instance associated with this WorkManager.
Returns:
The Configuration instance associated with this WorkManager.
public java.util.List<Scheduler>
getSchedulers()
Returns:
The Schedulers associated with this WorkManager based on the device's
capabilities, SDK version, etc.
Returns:
The Processor used to process background work.
Returns:
the TaskExecutor used by the instance of WorkManager.
Returns:
the PreferenceUtils used by the instance of WorkManager.
Returns:
the Trackers used by WorkManager
public abstract
Operation enqueue(java.util.List<WorkRequest> requests)
Enqueues one or more items for background processing.
Parameters:
requests: One or more WorkRequest to enqueue
Returns:
An Operation that can be used to determine when the enqueue has completed
public abstract
WorkContinuation beginWith(java.util.List<OneTimeWorkRequest> work)
Begins a chain with one or more OneTimeWorkRequests, which can be enqueued together
in the future using WorkContinuation.enqueue().
If any work in the chain fails or is cancelled, all of its dependent work inherits that state
and will never run.
Parameters:
work: One or more OneTimeWorkRequest to start a chain of work
Returns:
A WorkContinuation that allows for further chaining of dependent
OneTimeWorkRequest
This method allows you to begin unique chains of work for situations where you only want one
chain with a given name to be active at a time. For example, you may only want one sync
operation to be active. If there is one pending, you can choose to let it run or replace it
with your new work.
The uniqueWorkName uniquely identifies this set of work.
If this method determines that new work should be enqueued and run, all records of previous
work with uniqueWorkName will be pruned. If this method determines that new work
should NOT be run, then the entire chain will be considered a no-op.
If any work in the chain fails or is cancelled, all of its dependent work inherits that state
and will never run. This is particularly important if you are using APPEND as your
ExistingWorkPolicy.
Parameters:
uniqueWorkName: A unique name which for this chain of work
existingWorkPolicy: An ExistingWorkPolicy; see below for more information
work: One or more OneTimeWorkRequest to enqueue. REPLACE ensures that
if there is pending work labelled with uniqueWorkName, it will be
cancelled and the new work will run. KEEP will run the new sequence of
work only if there is no pending work labelled with uniqueWorkName.
APPEND will create a new sequence of work if there is no
existing work with uniqueWorkName; otherwise, work will be added
as a child of all leaf nodes labelled with uniqueWorkName.
Returns:
A WorkContinuation that allows further chaining
public abstract
Operation enqueueUniqueWork(java.lang.String uniqueWorkName,
ExistingWorkPolicy existingWorkPolicy, java.util.List<OneTimeWorkRequest> work)
This method allows you to enqueue work requests to a uniquely-named
WorkContinuation, where only one continuation of a particular name can be active at
a time. For example, you may only want one sync operation to be active. If there is one
pending, you can choose to let it run or replace it with your new work.
The uniqueWorkName uniquely identifies this WorkContinuation.
Parameters:
uniqueWorkName: A unique name which for this operation
existingWorkPolicy: An ExistingWorkPolicy
work: OneTimeWorkRequests to enqueue. REPLACE ensures
that if there is pending work labelled with uniqueWorkName, it
will be cancelled and the new work will run. KEEP will run the
new OneTimeWorkRequests only if there is no pending work labelled with
uniqueWorkName. APPEND will append the
OneTimeWorkRequests as leaf nodes labelled with uniqueWorkName.
Returns:
An Operation that can be used to determine when the enqueue has completed
This method allows you to enqueue a uniquely-named PeriodicWorkRequest, where only
one PeriodicWorkRequest of a particular name can be active at a time. For example, you may
only want one sync operation to be active. If there is one pending, you can choose to let it
run or replace it with your new work.
The uniqueWorkName uniquely identifies this PeriodicWorkRequest.
Parameters:
uniqueWorkName: A unique name which for this operation
existingPeriodicWorkPolicy: An ExistingPeriodicWorkPolicy
periodicWork: A PeriodicWorkRequest to enqueue. REPLACE ensures that if
there is pending work labelled with uniqueWorkName, it will be
cancelled and the new work will run. KEEP will run the new
PeriodicWorkRequest only if there is no pending work labelled with
uniqueWorkName.
Returns:
An Operation that can be used to determine when the enqueue has completed
Creates a WorkContinuation for the given unique PeriodicWorkRequest.
public abstract
Operation cancelWorkById(java.util.UUID id)
Cancels work with the given id if it isn't finished. Note that cancellation is a best-effort
policy and work that is already executing may continue to run. Upon cancellation,
ListenableWorker.onStopped() will be invoked for any affected workers.
Parameters:
id: The id of the work
Returns:
An Operation that can be used to determine when the cancelWorkById has
completed
public abstract
Operation cancelAllWorkByTag(java.lang.String tag)
Cancels all unfinished work with the given tag. Note that cancellation is a best-effort
policy and work that is already executing may continue to run. Upon cancellation,
ListenableWorker.onStopped() will be invoked for any affected workers.
Parameters:
tag: The tag used to identify the work
Returns:
An Operation that can be used to determine when the cancelAllWorkByTag has
completed
public abstract
Operation cancelUniqueWork(java.lang.String uniqueWorkName)
Cancels all unfinished work in the work chain with the given name. Note that cancellation is
a best-effort policy and work that is already executing may continue to run. Upon
cancellation, ListenableWorker.onStopped() will be invoked for any affected workers.
Parameters:
uniqueWorkName: The unique name used to identify the chain of work
Returns:
An Operation that can be used to determine when the cancelUniqueWork has
completed
Cancels all unfinished work. Use this method with extreme caution! By invoking it,
you will potentially affect other modules or libraries in your codebase. It is strongly
recommended that you use one of the other cancellation methods at your disposal.
Upon cancellation, ListenableWorker.onStopped() will be invoked for any affected
workers.
Returns:
An Operation that can be used to determine when the cancelAllWork has
completed
public abstract PendingIntent
createCancelPendingIntent(java.util.UUID id)
Creates a PendingIntent
which can be used to cancel a WorkRequest with the
given id.
Parameters:
id: The WorkRequest id.
Returns:
The PendingIntent
that can be used to cancel the WorkRequest.
public abstract
LiveData<java.lang.Long>
getLastCancelAllTimeMillisLiveData()
Gets a LiveData of the last time all work was cancelled. This method is intended for
use by library and module developers who have dependent data in their own repository that
must be updated or deleted in case someone cancels their work without their prior knowledge.
Returns:
A LiveData of the timestamp (System#getCurrentTimeMillis()) when
WorkManager.cancelAllWork() was last invoked; this timestamp may be 0L if this
never occurred
public abstract <any>
getLastCancelAllTimeMillis()
Gets a of the last time all work was cancelled. This method is
intended for use by library and module developers who have dependent data in their own
repository that must be updated or deleted in case someone cancels their work without
their prior knowledge.
Returns:
A of the timestamp (System#getCurrentTimeMillis())
when WorkManager.cancelAllWork() was last invoked; this timestamp may be 0L if
this never occurred
Prunes all eligible finished work from the internal database. Eligible work must be finished
(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, or
WorkInfo.State.CANCELLED), with zero unfinished dependents.
Use this method with caution; by invoking it, you (and any modules and libraries in
your codebase) will no longer be able to observe the WorkInfo of the pruned work.
You do not normally need to call this method - WorkManager takes care to auto-prune its work
after a sane period of time. This method also ignores the
WorkRequest.Builder.keepResultsForAtLeast(long, TimeUnit) policy.
Returns:
An Operation that can be used to determine when the pruneWork has
completed
public abstract
LiveData<WorkInfo>
getWorkInfoByIdLiveData(java.util.UUID id)
Gets a LiveData of the WorkInfo for a given work id.
Parameters:
id: The id of the work
Returns:
A LiveData of the WorkInfo associated with id; note that
this WorkInfo may be null if id is not known to
WorkManager.
public <any>
getWorkInfoByIdFlow(java.util.UUID id)
public abstract <any>
getWorkInfoById(java.util.UUID id)
Gets a of the WorkInfo for a given work id.
Parameters:
id: The id of the work
Returns:
A of the WorkInfo associated with id;
note that this WorkInfo may be null if id is not known to
WorkManager
public <any>
getWorkInfosByTagFlow(java.lang.String tag)
public abstract
LiveData<java.util.List>
getWorkInfosByTagLiveData(java.lang.String tag)
Gets a LiveData of the WorkInfo for all work for a given tag.
Parameters:
tag: The tag of the work
Returns:
A LiveData list of WorkInfo for work tagged with tag
public abstract <any>
getWorkInfosByTag(java.lang.String tag)
Gets a of the WorkInfo for all work for a given tag.
Parameters:
tag: The tag of the work
Returns:
A list of WorkInfo for work tagged with
tag
public abstract
LiveData<java.util.List>
getWorkInfosForUniqueWorkLiveData(java.lang.String uniqueWorkName)
Gets a LiveData of the WorkInfo for all work in a work chain with a given
unique name.
Parameters:
uniqueWorkName: The unique name used to identify the chain of work
Returns:
A LiveData of the WorkInfo for work in the chain named
uniqueWorkName
public <any>
getWorkInfosForUniqueWorkFlow(java.lang.String uniqueWorkName)
public abstract <any>
getWorkInfosForUniqueWork(java.lang.String uniqueWorkName)
Gets a of the WorkInfo for all work in a work chain
with a given unique name.
Parameters:
uniqueWorkName: The unique name used to identify the chain of work
Returns:
A of the WorkInfo for work in the chain named
uniqueWorkName
Gets the LiveData of the java.util.List
of WorkInfo for all work
referenced by the WorkQuery specification.
Parameters:
workQuery: The work query specification
Returns:
A LiveData of the java.util.List
of WorkInfo for work
referenced by this WorkQuery.
public <any>
getWorkInfosFlow(
WorkQuery workQuery)
public abstract <any>
getWorkInfos(
WorkQuery workQuery)
Gets the of the java.util.List
of WorkInfo for all work
referenced by the WorkQuery specification.
Parameters:
workQuery: The work query specification
Returns:
A of the java.util.List
of WorkInfo for work
referenced by this WorkQuery.
public void
stopForegroundWork(WorkGenerationalId id)
Parameters:
id: The WorkSpec id to stop when running in the context of a
foreground service.
public void
rescheduleEligibleWork()
Reschedules all the eligible work. Useful for cases like, app was force stopped or
BOOT_COMPLETED, TIMEZONE_CHANGED and TIME_SET for AlarmManager.
public void
onForceStopRunnableCompleted()
A way for ForceStopRunnable to tell WorkManagerImpl that it has completed.
public void
setReschedulePendingResult(BroadcastReceiver.PendingResult rescheduleReceiverResult)
This method is invoked by
RescheduleReceiver
after a call to BroadcastReceiver
. Once ForceStopRunnable is done,
we can safely call .
public void
closeDatabase()
Cancels workmanager's scope and closes the database
Source
/*
* Copyright 2017 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.impl;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.text.TextUtils.isEmpty;
import static androidx.work.ListenableFutureKt.executeAsync;
import static androidx.work.impl.UnfinishedWorkListenerKt.maybeLaunchUnfinishedWorkListener;
import static androidx.work.impl.WorkManagerImplExtKt.createWorkManager;
import static androidx.work.impl.WorkManagerImplExtKt.createWorkManagerScope;
import static androidx.work.impl.WorkerUpdater.enqueueUniquelyNamedPeriodic;
import static androidx.work.impl.foreground.SystemForegroundDispatcher.createCancelWorkIntent;
import static androidx.work.impl.model.RawWorkInfoDaoKt.getWorkInfoPojosFlow;
import static androidx.work.impl.model.WorkSpecDaoKt.getWorkStatusPojoFlowDataForIds;
import static androidx.work.impl.model.WorkSpecDaoKt.getWorkStatusPojoFlowForName;
import static androidx.work.impl.model.WorkSpecDaoKt.getWorkStatusPojoFlowForTag;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.arch.core.util.Function;
import androidx.lifecycle.LiveData;
import androidx.work.Configuration;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.Logger;
import androidx.work.OneTimeWorkRequest;
import androidx.work.Operation;
import androidx.work.PeriodicWorkRequest;
import androidx.work.TracerKt;
import androidx.work.WorkContinuation;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.work.WorkQuery;
import androidx.work.WorkRequest;
import androidx.work.impl.background.systemalarm.RescheduleReceiver;
import androidx.work.impl.background.systemjob.SystemJobScheduler;
import androidx.work.impl.constraints.trackers.Trackers;
import androidx.work.impl.model.RawWorkInfoDao;
import androidx.work.impl.model.WorkGenerationalId;
import androidx.work.impl.model.WorkSpec;
import androidx.work.impl.model.WorkSpecDao;
import androidx.work.impl.utils.CancelWorkRunnable;
import androidx.work.impl.utils.ForceStopRunnable;
import androidx.work.impl.utils.LiveDataUtils;
import androidx.work.impl.utils.PreferenceUtils;
import androidx.work.impl.utils.PruneWorkRunnableKt;
import androidx.work.impl.utils.RawQueries;
import androidx.work.impl.utils.StatusRunnable;
import androidx.work.impl.utils.StopWorkRunnable;
import androidx.work.impl.utils.taskexecutor.TaskExecutor;
import androidx.work.multiprocess.RemoteWorkManager;
import com.google.common.util.concurrent.ListenableFuture;
import kotlin.Unit;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.flow.Flow;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
* A concrete implementation of {@link WorkManager}.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerImpl extends WorkManager {
private static final String TAG = Logger.tagWithPrefix("WorkManagerImpl");
public static final int MAX_PRE_JOB_SCHEDULER_API_LEVEL = 22;
public static final int MIN_JOB_SCHEDULER_API_LEVEL = 23;
public static final int CONTENT_URI_TRIGGER_API_LEVEL = 24;
public static final String REMOTE_WORK_MANAGER_CLIENT =
"androidx.work.multiprocess.RemoteWorkManagerClient";
private Context mContext;
private Configuration mConfiguration;
private WorkDatabase mWorkDatabase;
private TaskExecutor mWorkTaskExecutor;
private List<Scheduler> mSchedulers;
private Processor mProcessor;
private PreferenceUtils mPreferenceUtils;
private boolean mForceStopRunnableCompleted = false;
private BroadcastReceiver.PendingResult mRescheduleReceiverResult;
private volatile RemoteWorkManager mRemoteWorkManager;
private final Trackers mTrackers;
/**
* Job for the scope of the whole WorkManager
*/
private final CoroutineScope mWorkManagerScope;
private static WorkManagerImpl sDelegatedInstance = null;
private static WorkManagerImpl sDefaultInstance = null;
private static final Object sLock = new Object();
/**
* @param delegate The delegate for {@link WorkManagerImpl} for testing; {@code null} to use the
* default instance
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void setDelegate(@Nullable WorkManagerImpl delegate) {
synchronized (sLock) {
sDelegatedInstance = delegate;
}
}
/**
* Retrieves the singleton instance of {@link WorkManagerImpl}.
*
* @return The singleton instance of {@link WorkManagerImpl}
* @deprecated Call {@link WorkManagerImpl#getInstance(Context)} instead.
*/
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@SuppressWarnings("NullableProblems")
public static @Nullable WorkManagerImpl getInstance() {
synchronized (sLock) {
if (sDelegatedInstance != null) {
return sDelegatedInstance;
}
return sDefaultInstance;
}
}
/**
*
*/
@SuppressWarnings("deprecation")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static boolean isInitialized() {
WorkManagerImpl instance = getInstance();
return instance != null;
}
/**
* Retrieves the singleton instance of {@link WorkManagerImpl}.
*
* @param context A context for on-demand initialization.
* @return The singleton instance of {@link WorkManagerImpl}
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
WorkManagerImpl instance = getInstance();
if (instance == null) {
Context appContext = context.getApplicationContext();
if (appContext instanceof Configuration.Provider) {
initialize(
appContext,
((Configuration.Provider) appContext).getWorkManagerConfiguration());
instance = getInstance(appContext);
} else {
throw new IllegalStateException("WorkManager is not initialized properly. You "
+ "have explicitly disabled WorkManagerInitializer in your manifest, "
+ "have not manually called WorkManager#initialize at this point, and "
+ "your Application does not implement Configuration.Provider.");
}
}
return instance;
}
}
/**
* Initializes the singleton instance of {@link WorkManagerImpl}. You should only do this if
* you want to use a custom {@link Configuration} object and have disabled
* WorkManagerInitializer.
*
* @param context A {@link Context} object for configuration purposes. Internally, this
* class will call {@link Context#getApplicationContext()}, so you may
* safely pass in any Context without risking a memory leak.
* @param configuration The {@link Configuration} for used to set up WorkManager.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("WorkManager is already initialized. Did you "
+ "try to initialize it manually without disabling "
+ "WorkManagerInitializer? See "
+ "WorkManager#initialize(Context, Configuration) or the class level "
+ "Javadoc for more information.");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = createWorkManager(context, configuration);
}
sDelegatedInstance = sDefaultInstance;
}
}
}
/**
* Create an instance of {@link WorkManagerImpl}.
*
* @param context The application {@link Context}
* @param configuration The {@link Configuration} configuration
* @param workTaskExecutor The {@link TaskExecutor} for running "processing" jobs, such as
* enqueueing, scheduling, cancellation, etc.
* @param workDatabase The {@link WorkDatabase} instance
* @param processor The {@link Processor} instance
* @param trackers Trackers
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase workDatabase,
@NonNull List<Scheduler> schedulers,
@NonNull Processor processor,
@NonNull Trackers trackers) {
context = context.getApplicationContext();
// Check for direct boot mode
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Api24Impl.isDeviceProtectedStorage(
context)) {
throw new IllegalStateException("Cannot initialize WorkManager in direct boot mode");
}
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
mContext = context;
mWorkTaskExecutor = workTaskExecutor;
mWorkDatabase = workDatabase;
mProcessor = processor;
mTrackers = trackers;
mConfiguration = configuration;
mSchedulers = schedulers;
mWorkManagerScope = createWorkManagerScope(mWorkTaskExecutor);
mPreferenceUtils = new PreferenceUtils(mWorkDatabase);
Schedulers.registerRescheduling(schedulers, mProcessor,
workTaskExecutor.getSerialTaskExecutor(), mWorkDatabase, configuration);
// Checks for app force stops.
mWorkTaskExecutor.executeOnTaskThread(new ForceStopRunnable(context, this));
maybeLaunchUnfinishedWorkListener(mWorkManagerScope, mContext, configuration, workDatabase);
}
/**
* @return The application {@link Context} associated with this WorkManager.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public Context getApplicationContext() {
return mContext;
}
/**
* @return The {@link WorkDatabase} instance associated with this WorkManager.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public WorkDatabase getWorkDatabase() {
return mWorkDatabase;
}
/**
* @return workmanager's CoroutineScope
*/
@NonNull
CoroutineScope getWorkManagerScope() {
return mWorkManagerScope;
}
/**
* @return The {@link Configuration} instance associated with this WorkManager.
*/
@NonNull
@Override
public Configuration getConfiguration() {
return mConfiguration;
}
/**
* @return The {@link Scheduler}s associated with this WorkManager based on the device's
* capabilities, SDK version, etc.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull List<Scheduler> getSchedulers() {
return mSchedulers;
}
/**
* @return The {@link Processor} used to process background work.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull Processor getProcessor() {
return mProcessor;
}
/**
* @return the {@link TaskExecutor} used by the instance of {@link WorkManager}.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull TaskExecutor getWorkTaskExecutor() {
return mWorkTaskExecutor;
}
/**
* @return the {@link PreferenceUtils} used by the instance of {@link WorkManager}.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @NonNull PreferenceUtils getPreferenceUtils() {
return mPreferenceUtils;
}
/**
* @return the {@link Trackers} used by {@link WorkManager}
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public Trackers getTrackers() {
return mTrackers;
}
@Override
@NonNull
public Operation enqueue(
@NonNull List<? extends WorkRequest> requests) {
// This error is not being propagated as part of the Operation, as we want the
// app to crash during development. Having no workRequests is always a developer error.
if (requests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
return new WorkContinuationImpl(this, requests).enqueue();
}
@Override
public @NonNull WorkContinuation beginWith(@NonNull List<OneTimeWorkRequest> requests) {
if (requests.isEmpty()) {
throw new IllegalArgumentException(
"beginWith needs at least one OneTimeWorkRequest.");
}
return new WorkContinuationImpl(this, requests);
}
@Override
public @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List<OneTimeWorkRequest> requests) {
if (requests.isEmpty()) {
throw new IllegalArgumentException(
"beginUniqueWork needs at least one OneTimeWorkRequest.");
}
return new WorkContinuationImpl(this, uniqueWorkName, existingWorkPolicy, requests);
}
@NonNull
@Override
public Operation enqueueUniqueWork(@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List<OneTimeWorkRequest> requests) {
return new WorkContinuationImpl(this, uniqueWorkName,
existingWorkPolicy, requests).enqueue();
}
@Override
@NonNull
public Operation enqueueUniquePeriodicWork(
@NonNull String uniqueWorkName,
@NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,
@NonNull PeriodicWorkRequest request) {
if (existingPeriodicWorkPolicy == ExistingPeriodicWorkPolicy.UPDATE) {
return enqueueUniquelyNamedPeriodic(this, uniqueWorkName, request);
}
return createWorkContinuationForUniquePeriodicWork(
uniqueWorkName,
existingPeriodicWorkPolicy,
request)
.enqueue();
}
/**
* Creates a {@link WorkContinuation} for the given unique {@link PeriodicWorkRequest}.
*/
@NonNull
public WorkContinuationImpl createWorkContinuationForUniquePeriodicWork(
@NonNull String uniqueWorkName,
@NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,
@NonNull PeriodicWorkRequest periodicWork) {
ExistingWorkPolicy existingWorkPolicy;
if (existingPeriodicWorkPolicy == ExistingPeriodicWorkPolicy.KEEP) {
existingWorkPolicy = ExistingWorkPolicy.KEEP;
} else {
existingWorkPolicy = ExistingWorkPolicy.REPLACE;
}
return new WorkContinuationImpl(
this,
uniqueWorkName,
existingWorkPolicy,
Collections.singletonList(periodicWork));
}
@Override
public @NonNull Operation cancelWorkById(@NonNull UUID id) {
return CancelWorkRunnable.forId(id, this);
}
@Override
public @NonNull Operation cancelAllWorkByTag(@NonNull final String tag) {
return CancelWorkRunnable.forTag(tag, this);
}
@Override
@NonNull
public Operation cancelUniqueWork(@NonNull String uniqueWorkName) {
return CancelWorkRunnable.forName(uniqueWorkName, this);
}
@Override
public @NonNull Operation cancelAllWork() {
return CancelWorkRunnable.forAll(this);
}
@NonNull
@Override
public PendingIntent createCancelPendingIntent(@NonNull UUID id) {
Intent intent = createCancelWorkIntent(mContext, id.toString());
int flags = FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= 31) {
flags |= FLAG_MUTABLE;
}
return PendingIntent.getService(mContext, 0, intent, flags);
}
@Override
public @NonNull LiveData<Long> getLastCancelAllTimeMillisLiveData() {
return mPreferenceUtils.getLastCancelAllTimeMillisLiveData();
}
@Override
public @NonNull ListenableFuture<Long> getLastCancelAllTimeMillis() {
final PreferenceUtils preferenceUtils = mPreferenceUtils;
return executeAsync(mWorkTaskExecutor.getSerialTaskExecutor(),
"getLastCancelAllTimeMillis", preferenceUtils::getLastCancelAllTimeMillis);
}
@Override
public @NonNull Operation pruneWork() {
return PruneWorkRunnableKt.pruneWork(mWorkDatabase, mConfiguration, mWorkTaskExecutor);
}
@Override
public @NonNull LiveData<WorkInfo> getWorkInfoByIdLiveData(@NonNull UUID id) {
WorkSpecDao dao = mWorkDatabase.workSpecDao();
LiveData<List<WorkSpec.WorkInfoPojo>> inputLiveData =
dao.getWorkStatusPojoLiveDataForIds(Collections.singletonList(id.toString()));
return LiveDataUtils.dedupedMappedLiveDataFor(inputLiveData,
new Function<List<WorkSpec.WorkInfoPojo>, WorkInfo>() {
@Override
public WorkInfo apply(List<WorkSpec.WorkInfoPojo> input) {
WorkInfo workInfo = null;
if (input != null && input.size() > 0) {
workInfo = input.get(0).toWorkInfo();
}
return workInfo;
}
},
mWorkTaskExecutor);
}
@NonNull
@Override
public Flow<WorkInfo> getWorkInfoByIdFlow(@NonNull UUID id) {
return getWorkStatusPojoFlowDataForIds(getWorkDatabase().workSpecDao(), id);
}
@Override
public @NonNull ListenableFuture<WorkInfo> getWorkInfoById(@NonNull UUID id) {
return StatusRunnable.forUUID(mWorkDatabase, mWorkTaskExecutor, id);
}
@NonNull
@Override
public Flow<List<WorkInfo>> getWorkInfosByTagFlow(@NonNull String tag) {
WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
return getWorkStatusPojoFlowForTag(workSpecDao,
mWorkTaskExecutor.getTaskCoroutineDispatcher(), tag);
}
@Override
public @NonNull LiveData<List<WorkInfo>> getWorkInfosByTagLiveData(@NonNull String tag) {
WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
LiveData<List<WorkSpec.WorkInfoPojo>> inputLiveData =
workSpecDao.getWorkStatusPojoLiveDataForTag(tag);
return LiveDataUtils.dedupedMappedLiveDataFor(
inputLiveData,
WorkSpec.WORK_INFO_MAPPER,
mWorkTaskExecutor);
}
@Override
public @NonNull ListenableFuture<List<WorkInfo>> getWorkInfosByTag(@NonNull String tag) {
return StatusRunnable.forTag(mWorkDatabase, mWorkTaskExecutor, tag);
}
@Override
@NonNull
public LiveData<List<WorkInfo>> getWorkInfosForUniqueWorkLiveData(
@NonNull String uniqueWorkName) {
WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
LiveData<List<WorkSpec.WorkInfoPojo>> inputLiveData =
workSpecDao.getWorkStatusPojoLiveDataForName(uniqueWorkName);
return LiveDataUtils.dedupedMappedLiveDataFor(
inputLiveData,
WorkSpec.WORK_INFO_MAPPER,
mWorkTaskExecutor);
}
@NonNull
@Override
public Flow<List<WorkInfo>> getWorkInfosForUniqueWorkFlow(@NonNull String uniqueWorkName) {
WorkSpecDao workSpecDao = mWorkDatabase.workSpecDao();
return getWorkStatusPojoFlowForName(workSpecDao,
mWorkTaskExecutor.getTaskCoroutineDispatcher(), uniqueWorkName);
}
@Override
@NonNull
public ListenableFuture<List<WorkInfo>> getWorkInfosForUniqueWork(
@NonNull String uniqueWorkName) {
return StatusRunnable.forUniqueWork(mWorkDatabase, mWorkTaskExecutor, uniqueWorkName);
}
@NonNull
@Override
public LiveData<List<WorkInfo>> getWorkInfosLiveData(
@NonNull WorkQuery workQuery) {
RawWorkInfoDao rawWorkInfoDao = mWorkDatabase.rawWorkInfoDao();
LiveData<List<WorkSpec.WorkInfoPojo>> inputLiveData =
rawWorkInfoDao.getWorkInfoPojosLiveData(
RawQueries.toRawQuery(workQuery));
return LiveDataUtils.dedupedMappedLiveDataFor(
inputLiveData,
WorkSpec.WORK_INFO_MAPPER,
mWorkTaskExecutor);
}
@NonNull
@Override
public Flow<List<WorkInfo>> getWorkInfosFlow(@NonNull WorkQuery workQuery) {
RawWorkInfoDao rawWorkInfoDao = mWorkDatabase.rawWorkInfoDao();
return getWorkInfoPojosFlow(rawWorkInfoDao, mWorkTaskExecutor.getTaskCoroutineDispatcher(),
RawQueries.toRawQuery(workQuery));
}
@NonNull
@Override
public ListenableFuture<List<WorkInfo>> getWorkInfos(@NonNull WorkQuery workQuery) {
return StatusRunnable.forWorkQuerySpec(mWorkDatabase, mWorkTaskExecutor, workQuery);
}
@NonNull
@Override
public ListenableFuture<UpdateResult> updateWork(@NonNull WorkRequest request) {
return WorkerUpdater.updateWorkImpl(this, request);
}
LiveData<List<WorkInfo>> getWorkInfosById(@NonNull List<String> workSpecIds) {
WorkSpecDao dao = mWorkDatabase.workSpecDao();
LiveData<List<WorkSpec.WorkInfoPojo>> inputLiveData =
dao.getWorkStatusPojoLiveDataForIds(workSpecIds);
return LiveDataUtils.dedupedMappedLiveDataFor(
inputLiveData,
WorkSpec.WORK_INFO_MAPPER,
mWorkTaskExecutor);
}
/**
*
*/
@Nullable
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public RemoteWorkManager getRemoteWorkManager() {
if (mRemoteWorkManager == null) {
synchronized (sLock) {
if (mRemoteWorkManager == null) {
// Initialize multi-process support.
tryInitializeMultiProcessSupport();
if (mRemoteWorkManager == null && !isEmpty(
mConfiguration.getDefaultProcessName())) {
String message = "Invalid multiprocess configuration. Define an "
+ "`implementation` dependency on :work:work-multiprocess library";
throw new IllegalStateException(message);
}
}
}
}
return mRemoteWorkManager;
}
/**
* @param id The {@link WorkSpec} id to stop when running in the context of a
* foreground service.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void stopForegroundWork(@NonNull WorkGenerationalId id) {
mWorkTaskExecutor.executeOnTaskThread(new StopWorkRunnable(mProcessor,
new StartStopToken(id), true));
}
/**
* Reschedules all the eligible work. Useful for cases like, app was force stopped or
* BOOT_COMPLETED, TIMEZONE_CHANGED and TIME_SET for AlarmManager.
*/
public void rescheduleEligibleWork() {
// Delegate to the getter so mocks continue to work when testing.
Configuration configuration = getConfiguration();
TracerKt.traced(configuration.getTracer(), "ReschedulingWork", () -> {
// This gives us an easy way to clear persisted work state, and then reschedule work
// that WorkManager is aware of. Ideally, we do something similar for other
// persistent schedulers.
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
SystemJobScheduler.cancelAllInAllNamespaces(getApplicationContext());
}
// Reset scheduled state.
getWorkDatabase().workSpecDao().resetScheduledState();
// Delegate to the WorkManager's schedulers.
// Using getters here so we can use from a mocked instance
// of WorkManagerImpl.
Schedulers.schedule(getConfiguration(), getWorkDatabase(), getSchedulers());
return Unit.INSTANCE;
});
}
/**
* A way for {@link ForceStopRunnable} to tell {@link WorkManagerImpl} that it has completed.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void onForceStopRunnableCompleted() {
synchronized (sLock) {
mForceStopRunnableCompleted = true;
if (mRescheduleReceiverResult != null) {
mRescheduleReceiverResult.finish();
mRescheduleReceiverResult = null;
}
}
}
/**
* This method is invoked by
* {@link RescheduleReceiver}
* after a call to {@link BroadcastReceiver#goAsync()}. Once {@link ForceStopRunnable} is done,
* we can safely call {@link BroadcastReceiver.PendingResult#finish()}.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setReschedulePendingResult(
@NonNull BroadcastReceiver.PendingResult rescheduleReceiverResult) {
synchronized (sLock) {
// if we have two broadcast in the row, finish old one and use new one
if (mRescheduleReceiverResult != null) {
mRescheduleReceiverResult.finish();
}
mRescheduleReceiverResult = rescheduleReceiverResult;
if (mForceStopRunnableCompleted) {
mRescheduleReceiverResult.finish();
mRescheduleReceiverResult = null;
}
}
}
/**
* Cancels workmanager's scope and closes the database
*/
public void closeDatabase() {
WorkManagerImplExtKt.close(this);
}
/**
* Tries to find a multi-process safe implementation for {@link WorkManager}.
*/
private void tryInitializeMultiProcessSupport() {
try {
Class<?> klass = Class.forName(REMOTE_WORK_MANAGER_CLIENT);
mRemoteWorkManager = (RemoteWorkManager) klass.getConstructor(
Context.class, WorkManagerImpl.class
).newInstance(mContext, this);
} catch (Throwable throwable) {
Logger.get().debug(TAG, "Unable to initialize multi-process support", throwable);
}
}
@RequiresApi(24)
static class Api24Impl {
private Api24Impl() {
// This class is not instantiable.
}
static boolean isDeviceProtectedStorage(Context context) {
return context.isDeviceProtectedStorage();
}
}
}