介紹
WorkManager在后臺完成異步任務(wù),即使手機重啟,app進程被關(guān)閉掉,WorkManager也會執(zhí)行設(shè)置的異步任務(wù)。
專門用來處理非及時周期性任務(wù)的,一定會執(zhí)行。
SDK版本在28 及以上才支持。
WorkManager的四個角色:
任務(wù)Worker:該角色主要負責執(zhí)行耗時的異步操作,需要繼承抽象類Worker,實現(xiàn)dowork抽象方法。
約束Constraint: 該角色主要是指定了耗時異步worker執(zhí)行的最佳的條件
數(shù)據(jù)Data: 該角色是在后臺耗時任務(wù)與應(yīng)用進程間進行數(shù)據(jù)交互的數(shù)據(jù)模型
Requst: 將任務(wù)、約束、數(shù)據(jù)等封裝成一個執(zhí)行請求,然后通過加入WorkManager 隊列
二、用法
1、加入依賴
//workmanager
def work_version = "2.5.0"
implementation "androidx.work:work-runtime:$work_version"
2、Worker任務(wù)
1) 必須繼承Worker,需要重寫doWork()方法;在doWork方法中做異步的操作。
public class CustomWork extends Worker {
public static final String TAG = CustomWork.class.getSimpleName();
public CustomWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.e(TAG, "doWork: start");
//下面是簡單的異步任務(wù)
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
return Result.failure();
} finally {
Log.e(TAG, "doWork: end");
}
return Result.success();
}
}
3、創(chuàng)建WorkRequest
1)單次Request
任務(wù)執(zhí)行完就結(jié)束。
OneTimeWorkRequest oneTimeWorkRequest = OneTimeWorkRequest.from(CustomWork.class);
2) 重復(fù)Request
每隔多久就執(zhí)行以下Worker任務(wù)。這里的時間間隔必須是大于等于15分鐘,如果小于15分鐘的時間間隔,WorkManager會自動間隔15分鐘后執(zhí)行。
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(CustomWork.class, 15, TimeUnit.MINUTES).build();
4、添加約束
給任務(wù)的執(zhí)行添加一些約束條件,這樣任務(wù)就會在約束條件滿足的情況下才會執(zhí)行worker,否則不執(zhí)行。這樣就可以確保將延遲任務(wù)在最佳條件下運行。
1.網(wǎng)絡(luò)約束NetworkType:約束運行工作所需的網(wǎng)絡(luò)類
2.低電量約束BatteryNotLow:如果設(shè)置為 true,那么當設(shè)備處于“電量不足模式”時,工作不會運行
3.充電約束RequiresCharging:如果設(shè)置為 true,那么工作只能在設(shè)備充電時運行
4.空閑狀態(tài)約束DeviceIdle:如果設(shè)置為 true,則要求用戶的設(shè)備必須處于空閑狀態(tài),才能運行工作。
5.存儲空間約束StorageNotLow:如果設(shè)置為 true,那么當用戶設(shè)備上的存儲空間不足時,工作不會運行。
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)//聯(lián)網(wǎng)中
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)//充電中
.setRequiresDeviceIdle(true)//空閑時
.build();
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(CustomWork.class)
.setConstraints(constraints).build();
5、傳遞數(shù)據(jù)
調(diào)用方傳遞數(shù)據(jù)給Worker:
Data data = new Data.Builder().putString("leon", "I am from WorkManagerMainActivity").build();
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(CustomWork.class)
.setInputData(data).build();
調(diào)用方接收Worker回傳回來的數(shù)據(jù):
//獲取任務(wù)返回的數(shù)據(jù)
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.getId()).observe(this, workInfo ->
{
if (workInfo.getState().isFinished()) {
String backleon = workInfo.getOutputData().getString("leon");
Log.e(TAG, "任務(wù)返回的數(shù)據(jù):" + backleon);
}
}
);
Worker中接收數(shù)據(jù):
//獲取傳遞的數(shù)據(jù)
String leon = getInputData().getString("leon");
Log.e(TAG, "傳遞進來的數(shù)據(jù): " + leon);
Worker任務(wù)執(zhí)行完后回傳數(shù)據(jù):
//返回Activity數(shù)據(jù)
Data data = new Data.Builder().putString("leon","task is completed").build();
return Result.success(data);
6、WorkRequest入隊
單個workRequest入隊:
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest);
多個順序WorkRequest入隊
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(TaskWork.class).build();
OneTimeWorkRequest oneTimeWorkRequest2 = new OneTimeWorkRequest.Builder(TaskWork2.class).build();
OneTimeWorkRequest oneTimeWorkRequest3 = new OneTimeWorkRequest.Builder(TaskWork3.class).build();
OneTimeWorkRequest oneTimeWorkRequest4 = new OneTimeWorkRequest.Builder(TaskWork4.class).build();
WorkManager.getInstance(this)
.beginWith(oneTimeWorkRequest)
.then(oneTimeWorkRequest2)
.then(oneTimeWorkRequest3)
.then(oneTimeWorkRequest4)
.enqueue();
三、原理
在創(chuàng)建WorkManager實例時,會將任務(wù)信息和約束條件都保存到Room數(shù)據(jù)庫中,并添加廣播接收器,將任務(wù)加入隊列時,會從數(shù)據(jù)庫中找到符合條件的任務(wù)開始執(zhí)行,并調(diào)回到自定義的異步任務(wù)中,執(zhí)行完成后將任務(wù)的狀態(tài)置為success ;當約束條件成立時,通過系統(tǒng)的SystemAlarmService服務(wù)來進行喚醒任務(wù)繼續(xù)執(zhí)行。
1、WorkManager實例化流程

WorkManager.java
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
WorkManagerImpl.java
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
//這里首先會獲取單例,說明一開始調(diào)用getInstance()時,有可能已經(jīng)創(chuàng)建好了???
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;
}
}
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) {
//這里是創(chuàng)建WorkManagerImpl的單例對象,
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
WorkManagerTaskExecutor.java
public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
// Wrap it with a serial executor so we have ordering guarantees on commands
// being executed.
mBackgroundExecutor = new SerialExecutor(backgroundExecutor);
}
SerialEecutor.java
主要的工作:
1、保存Task任務(wù)到隊里里面,
2、開啟一個線程,將任務(wù)隊列里面的任務(wù)按照順序交個Execuor來執(zhí)行
public class SerialExecutor implements Executor {
private final ArrayDeque<Task> mTasks;
private final Executor mExecutor;
private final Object mLock;
private volatile Runnable mActive;
public SerialExecutor(@NonNull Executor executor) {
mExecutor = executor;
mTasks = new ArrayDeque<>();
mLock = new Object();
}
@Override
public void execute(@NonNull Runnable command) {
synchronized (mLock) {
mTasks.add(new Task(this, command));
if (mActive == null) {
scheduleNext();
}
}
}
// Synthetic access
void scheduleNext() {
synchronized (mLock) {
if ((mActive = mTasks.poll()) != null) {
mExecutor.execute(mActive);
}
}
}
/**
* @return {@code true} if there are tasks to execute in the queue.
*/
public boolean hasPendingTasks() {
synchronized (mLock) {
return !mTasks.isEmpty();
}
}
@NonNull
@VisibleForTesting
public Executor getDelegatedExecutor() {
return mExecutor;
}
/**
* A {@link Runnable} which tells the {@link SerialExecutor} to schedule the next command
* after completion.
*/
static class Task implements Runnable {
final SerialExecutor mSerialExecutor;
final Runnable mRunnable;
Task(@NonNull SerialExecutor serialExecutor, @NonNull Runnable runnable) {
mSerialExecutor = serialExecutor;
mRunnable = runnable;
}
@Override
public void run() {
try {
mRunnable.run();
} finally {
mSerialExecutor.scheduleNext();
}
}
}
}
WorkManagerImpl.java構(gòu)造方法
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
WorkDataBase.java
創(chuàng)建Room數(shù)據(jù)庫,將任務(wù)信息全部保存到數(shù)據(jù)庫中,并給數(shù)據(jù)庫增加一些配置升級等信息。
public static WorkDatabase create(
@NonNull final Context context,
@NonNull Executor queryExecutor,
boolean useTestDatabase) {
RoomDatabase.Builder<WorkDatabase> builder;
if (useTestDatabase) {
builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
.allowMainThreadQueries();
} else {
String name = WorkDatabasePathHelper.getWorkDatabaseName();
builder = Room.databaseBuilder(context, WorkDatabase.class, name);
builder.openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
@NonNull
@Override
public SupportSQLiteOpenHelper create(
@NonNull SupportSQLiteOpenHelper.Configuration configuration) {
SupportSQLiteOpenHelper.Configuration.Builder configBuilder =
SupportSQLiteOpenHelper.Configuration.builder(context);
configBuilder.name(configuration.name)
.callback(configuration.callback)
.noBackupDirectory(true);
FrameworkSQLiteOpenHelperFactory factory =
new FrameworkSQLiteOpenHelperFactory();
return factory.create(configBuilder.build());
}
});
}
return builder.setQueryExecutor(queryExecutor)
.addCallback(generateCleanupCallback())
.addMigrations(WorkDatabaseMigrations.MIGRATION_1_2)
.addMigrations(
new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_2,
VERSION_3))
.addMigrations(MIGRATION_3_4)
.addMigrations(MIGRATION_4_5)
.addMigrations(
new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_5,
VERSION_6))
.addMigrations(MIGRATION_6_7)
.addMigrations(MIGRATION_7_8)
.addMigrations(MIGRATION_8_9)
.addMigrations(new WorkDatabaseMigrations.WorkMigration9To10(context))
.addMigrations(
new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_10,
VERSION_11))
.fallbackToDestructiveMigration()
.build();
}
2、將Worker加入enqueue流程

WorkManagerImpl.java
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
// 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 (workRequests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
return new WorkContinuationImpl(this, workRequests).enqueue();
}
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
WorkContinuationImpl.java
@Override
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
EnqueueRunnable.java
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
Schedulers.java
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
List<WorkSpec> allEligibleWorkSpecs;
workDatabase.beginTransaction();
try {
// Enqueued workSpecs when scheduling limits are applicable.
eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
// Enqueued workSpecs when scheduling limits are NOT applicable.
allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(
MAX_GREEDY_SCHEDULER_LIMIT);
if (eligibleWorkSpecsForLimitedSlots != null
&& eligibleWorkSpecsForLimitedSlots.size() > 0) {
long now = System.currentTimeMillis();
// Mark all the WorkSpecs as scheduled.
// Calls to Scheduler#schedule() could potentially result in more schedules
// on a separate thread. Therefore, this needs to be done first.
for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now);
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecsForLimitedSlots != null
&& eligibleWorkSpecsForLimitedSlots.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray =
new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
eligibleWorkSpecsArray =
eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
// Delegate to the underlying schedulers.
for (Scheduler scheduler : schedulers) {
if (scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
// Delegate to the underlying schedulers.
for (Scheduler scheduler : schedulers) {
if (!scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(enqueuedWorkSpecsArray);
}
}
}
}
GreedyScheduler.java
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mInDefaultProcess == null) {
checkDefaultProcess();
}
if (!mInDefaultProcess) {
Logger.get().info(TAG, "Ignoring schedule request in a secondary process");
return;
}
registerExecutionListenerIfNeeded();
// Keep track of the list of new WorkSpecs whose constraints need to be tracked.
// Add them to the known list of constrained WorkSpecs and call replace() on
// WorkConstraintsTracker. That way we only need to synchronize on the part where we
// are updating mConstrainedWorkSpecs.
Set<WorkSpec> constrainedWorkSpecs = new HashSet<>();
Set<String> constrainedWorkSpecIds = new HashSet<>();
for (WorkSpec workSpec : workSpecs) {
long nextRunTime = workSpec.calculateNextRunTime();
long now = System.currentTimeMillis();
if (workSpec.state == WorkInfo.State.ENQUEUED) {
if (now < nextRunTime) {
// Future work
if (mDelayedWorkTracker != null) {
mDelayedWorkTracker.schedule(workSpec);
}
} else if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
// Ignore requests that have an idle mode constraint.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
// Ignore requests that have content uri triggers.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
//這里就回到WorkManagerImpl.java的startWork()
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
// onExecuted() which is called on the main thread also modifies the list of mConstrained
// WorkSpecs. Therefore we need to lock here.
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
StartWorkRunnable.java
public void run() {
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
Processor.java
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
// Work may get triggered multiple times if they have passing constraints
// and new work with those constraints are added.
if (isEnqueued(id)) {
Logger.get().debug(
TAG,
String.format("Work %s is already enqueued for processing", id));
return false;
}
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
WorkWrapper.java
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
if (tryCheckForInterruptionAndResolve()) {
return;
}
mWorkDatabase.beginTransaction();
try {
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
if (mWorkSpec == null) {
Logger.get().error(
TAG,
String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
resolve(false);
mWorkDatabase.setTransactionSuccessful();
return;
}
// Do a quick check to make sure we don't need to bail out in case this work is already
// running, finished, or is blocked.
if (mWorkSpec.state != ENQUEUED) {
resolveIncorrectStatus();
mWorkDatabase.setTransactionSuccessful();
Logger.get().debug(TAG,
String.format("%s is not in ENQUEUED state. Nothing more to do.",
mWorkSpec.workerClassName));
return;
}
// Case 1:
// Ensure that Workers that are backed off are only executed when they are supposed to.
// GreedyScheduler can schedule WorkSpecs that have already been backed off because
// it is holding on to snapshots of WorkSpecs. So WorkerWrapper needs to determine
// if the ListenableWorker is actually eligible to execute at this point in time.
// Case 2:
// On API 23, we double scheduler Workers because JobScheduler prefers batching.
// So is the Work is periodic, we only need to execute it once per interval.
// Also potential bugs in the platform may cause a Job to run more than once.
if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
long now = System.currentTimeMillis();
// Allow first run of a PeriodicWorkRequest
// to go through. This is because when periodStartTime=0;
// calculateNextRunTime() always > now.
// For more information refer to b/124274584
boolean isFirstRun = mWorkSpec.periodStartTime == 0;
if (!isFirstRun && now < mWorkSpec.calculateNextRunTime()) {
Logger.get().debug(TAG,
String.format(
"Delaying execution for %s because it is being executed "
+ "before schedule.",
mWorkSpec.workerClassName));
// For AlarmManager implementation we need to reschedule this kind of Work.
// This is not a problem for JobScheduler because we will only reschedule
// work if JobScheduler is unaware of a jobId.
resolve(true);
mWorkDatabase.setTransactionSuccessful();
return;
}
}
// Needed for nested transactions, such as when we're in a dependent work request when
// using a SynchronousExecutor.
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
// Merge inputs. This can be potentially expensive code, so this should not be done inside
// a database transaction.
Data input;
if (mWorkSpec.isPeriodic()) {
input = mWorkSpec.input;
} else {
InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
String inputMergerClassName = mWorkSpec.inputMergerClassName;
InputMerger inputMerger =
inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
if (inputMerger == null) {
Logger.get().error(TAG, String.format("Could not create Input Merger %s",
mWorkSpec.inputMergerClassName));
setFailedAndResolve();
return;
}
List<Data> inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
}
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mWorkDatabase, mForegroundProcessor, mWorkTaskExecutor));
// Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
// in test mode.
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
if (mWorker == null) {
Logger.get().error(TAG,
String.format("Could not create Worker %s", mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
if (mWorker.isUsed()) {
Logger.get().error(TAG,
String.format("Received an already-used Worker %s; WorkerFactory should return "
+ "new instances",
mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
mWorker.setUsed();
// Try to set the work to the running state. Note that this may fail because another thread
// may have modified the DB since we checked last at the top of this function.
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
// Call mWorker.startWork() on the main thread.
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
Logger.get().debug(TAG, String.format("Starting work for %s",
mWorkSpec.workerClassName));
//這里會調(diào)用Worker的startWork()
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
// Avoid synthetic accessors.
final String workDescription = mWorkDescription;
future.addListener(new Runnable() {
@Override
@SuppressLint("SyntheticAccessor")
public void run() {
try {
// If the ListenableWorker returns a null result treat it as a failure.
ListenableWorker.Result result = future.get();
if (result == null) {
Logger.get().error(TAG, String.format(
"%s returned a null result. Treating it as a failure.",
mWorkSpec.workerClassName));
} else {
Logger.get().debug(TAG, String.format("%s returned a %s result.",
mWorkSpec.workerClassName, result));
mResult = result;
}
} catch (CancellationException exception) {
// Cancellations need to be treated with care here because innerFuture
// cancellations will bubble up, and we need to gracefully handle that.
Logger.get().info(TAG, String.format("%s was cancelled", workDescription),
exception);
} catch (InterruptedException | ExecutionException exception) {
Logger.get().error(TAG,
String.format("%s failed because it threw an exception/error",
workDescription), exception);
} finally {
onWorkFinished();
}
}
}, mWorkTaskExecutor.getBackgroundExecutor());
} else {
resolveIncorrectStatus();
}
}
Worker.java
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
//這里就回調(diào)到自定義的worker的dowork()方法中了
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
總結(jié)
技術(shù)點:
建造者模式、ROOM數(shù)據(jù)庫存放任務(wù)信息、線程同步,同步鎖機制,狀態(tài)機