史上最详细!那些你不知道的WorkManager流程分析和源码解析

//ForceStopRunnable

@Override

public void run() {

// Migrate the database to the no-backup directory if necessary.

WorkDatabasePathHelper.migrateDatabase(mContext);

// Clean invalid jobs attributed to WorkManager, and Workers that might have been

// interrupted because the application crashed (RUNNING state).

Logger.get().debug(TAG, "Performing cleanup operations.");

try {

boolean needsScheduling = cleanUp();

if (shouldRescheduleWorkers()) {

Logger.get().debug(TAG, "Rescheduling Workers.");

mWorkManager.rescheduleEligibleWork();

// Mark the jobs as migrated.

mWorkManager.getPreferenceUtils().setNeedsReschedule(false);

} else if (isForceStopped()) {

Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");

mWorkManager.rescheduleEligibleWork();

} else if (needsScheduling) {

Logger.get().debug(TAG, "Found unfinished work, scheduling it.");

Schedulers.schedule(

mWorkManager.getConfiguration(),

mWorkManager.getWorkDatabase(),

mWorkManager.getSchedulers());

}

mWorkManager.onForceStopRunnableCompleted();

} catch (SQLiteCantOpenDatabaseException

| SQLiteDatabaseCorruptException

| SQLiteAccessPermException exception) {

// ForceStopRunnable is usually the first thing that accesses a database (or an app's

// internal data directory). This means that weird PackageManager bugs are attributed

// to ForceStopRunnable, which is unfortunate. This gives the developer a better error

// message.

String message =

"The file system on the device is in a bad state. WorkManager cannot access "

  • "the app's internal data store.";
    Logger.get().error(TAG, message, exception);
    throw new IllegalStateException(message, exception);
    }
    }

这段代码的实现细节先不做深究。但是很明显,这个Runnable的作用就是在WorkManager初始化过程中,发现了未完成的,需要重新执行的任务,或者app被强制kill的情况下,直接对Scheduler进行调度。到此,一个WorkManager的初始化流程就完成了。

总结

  1. WorkManager的初始化是在app冷启动后,由WorkManagerInitializer这个ContentProvider执行的。
  2. 初始化过程包含了Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor等的初始化过程。
  3. Schedulers有两个。
    (1) GreedyScheduler:执行没有任何约束的非周期性的任务。
    (2) SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler:执行周期性或者有约束性的任务。优先返回SystemJobScheduler,在build version小于23的情况下先尝试返回GcmBasedScheduler,若返回为空再返回SystemAlarmScheduler。
  4. 初始化的最后,会根据情况找到需要被执行的任务进行调度执行。

WorkManager的初始化流程图:

4.1.2.WorkRequest的创建

梳理完WorkManager的初始化过程后,我们回到示例代码,创建一个OneTimeWorkRequest

val work1Request = OneTimeWorkRequestBuilder().build()

//OneTimeWorkRequest.Builder

/**

  • Creates a {@link OneTimeWorkRequest}.
  • @param workerClass The {@link ListenableWorker} class to run for this work
    */
    public Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
    super(workerClass);
    mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();
    }

//WorkRequest.Builder

Builder(@NonNull Class<? extends ListenableWorker> workerClass) {

mId = UUID.randomUUID();

mWorkerClass = workerClass;

mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());

addTag(workerClass.getName());

}

OneTimeWorkRequest为builder对象创建了WorkSpec对象用来保存任务id和类名,其中id是通过UUID自动生成的。request的tag默认是通过类名生成的,外部也可调用addTag()方法设置标签。另外为WorkSpec设置了默认的任务输入流的合并规则:OverwritingInputMerger。接着看build()方法的实现:

//WorkRequest.Builder

public final @NonNull W build() {

W returnValue = buildInternal();

// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.

mId = UUID.randomUUID();

mWorkSpec = new WorkSpec(mWorkSpec);

mWorkSpec.id = mId.toString();

return returnValue;

}

buildInternal()方法返回了一个WorkRequest对象,这是个抽象方法,在子类OneTimeWorkRequest.Builder中的实现如下:

//OneTimeWorkRequest.Builder

@Override

@NonNull OneTimeWorkRequest buildInternal() {

if (mBackoffCriteriaSet

&& Build.VERSION.SDK_INT >= 23

&& mWorkSpec.constraints.requiresDeviceIdle()) {

throw new IllegalArgumentException(

"Cannot set backoff criteria on an idle mode job");

}

if (mWorkSpec.runInForeground

&& Build.VERSION.SDK_INT >= 23

&& mWorkSpec.constraints.requiresDeviceIdle()) {

throw new IllegalArgumentException(

"Cannot run in foreground with an idle mode constraint");

}

return new OneTimeWorkRequest(this);

}

由于我们没有为WorkSpec设置其他属性,目前也没有约束条件,所以直接返回一个OneTimeWorkRequest对象。

//OneTimeWorkRequest

OneTimeWorkRequest(Builder builder) {

super(builder.mId, builder.mWorkSpec, builder.mTags);

}

把Builder的id, WorkSpec对象和tag赋给OneTimeWorkRequest对象。再回到Builder的build()方法:

//OneTimeWorkRequest.Builder

public final @NonNull W build() {

W returnValue = buildInternal();

// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.

mId = UUID.randomUUID();

mWorkSpec = new WorkSpec(mWorkSpec);

mWorkSpec.id = mId.toString();

return returnValue;

}

在buildInternal()拿到OneTimeWorkRequest对象之后,为Builder创建了一个新的WorkSpec对象,并赋予了新的UUID。虽然与原先的WorkSpec对象中每个属性的值是一致的,但指向了不同的内存地址。这么做的目的是为了这个Builder对象可被重复利用。好了,现在我们一个任务的WorkRequest创建就完成了。

总结

WorkRequest的创建是为了持有三个重要的成员变量。分别是:

  1. mId:由UUID生成的任务id。
  2. mWorkSpec:每个任务的属性。
  3. mTags:每个任务的标签。

WorkRequest创建的流程图

4.2非约束条件任务的执行过程

执行OneTimeWorkRequest

WorkManager.getInstance(this).enqueue(work1Request)

根据第一节的分析,WorkManager是个单例,在app启动的时候就已经被初始化了。所以直接看enqueue()的实现:

//WorkManager

@NonNull

public final Operation enqueue(@NonNull WorkRequest workRequest) {

return enqueue(Collections.singletonList(workRequest));

}

//WorkManager

@NonNull

public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);

//WorkManagerImpl

@NonNull

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();

}

创建一个WorkContinuationImpl()对象,再执行enqueue()方法。WorkContinuationImpl是WorkContinuation的子类。用来把多个OneTimeWorkRequest根据需求串行,并行或合并处理。我们熟悉的then(),combine(),enqueue()等都是这个类的方法。

//WorkContinuationImpl

WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,

String name,

ExistingWorkPolicy existingWorkPolicy,

@NonNull List<? extends WorkRequest> work,

@Nullable List parents) {

mWorkManagerImpl = workManagerImpl;

mName = name;

mExistingWorkPolicy = existingWorkPolicy;

mWork = work;

mParents = parents;

mIds = new ArrayList<>(mWork.size());

mAllIds = new ArrayList<>();

if (parents != null) {

for (WorkContinuationImpl parent : parents) {

mAllIds.addAll(parent.mAllIds);

}

}

for (int i = 0; i < work.size(); i++) {

String id = work.get(i).getStringId();

mIds.add(id);

mAllIds.add(id);

}

}

WorkContinuation保存了任务相关的所有信息,如WorkManager,WorkRequest,父WorkContinuation等。继续看WorkContinuationImpl的enqueue()方法的实现:

//WorkContinuationImpl

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

}

WorkManager的TaskExecutor执行了EnqueueRunnable。EnqueueRunnable中run()的实现:

//EnqueueRunnable

@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));

}

}

addToDatabase()的作用是把WorkSpec存入到数据库,并对任务的状态进行校验。当前的case会返回true。PackageManagerHelper.setComponentEnabled()开启了RescheduleReceiver。通过反编译我们得知这个Receiver是在AndroidManifest中注册的,默认是disable的。监听了开机,时间变化,时区变化这三个广播。

//AndroidManifest

scheduleWorkInBackground()的实现:

//EnqueueRunnable

/**

  • Schedules work on the background scheduler.
    */
    @VisibleForTesting
    public void scheduleWorkInBackground() {
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
    Schedulers.schedule(
    workManager.getConfiguration(),
    workManager.getWorkDatabase(),
    workManager.getSchedulers());
    }

这部分就是任务调度的实现。拿到Wor​

kManager对象后调用了Schedulers.schedule()方法,传入了Configuration, WorkDatabase, Scheduler这三个对象。执行schedule()方法:

//Schedulers

public static void schedule(

@NonNull Configuration configuration,

@NonNull WorkDatabase workDatabase,

List schedulers) {

if (schedulers == null || schedulers.size() == 0) {

return;

}

WorkSpecDao workSpecDao = workDatabase.workSpecDao();

List eligibleWorkSpecs;

workDatabase.beginTransaction();

try {

eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(

configuration.getMaxSchedulerLimit());

if (eligibleWorkSpecs != null && eligibleWorkSpecs.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 : eligibleWorkSpecs) {

workSpecDao.markWorkSpecScheduled(workSpec.id, now);

}

}

workDatabase.setTransactionSuccessful();

} finally {

workDatabase.endTransaction();

}

if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {

WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);

// Delegate to the underlying scheduler.

for (Scheduler scheduler : schedulers) {

scheduler.schedule(eligibleWorkSpecsArray);

}

}

}

先进行了一系列的数据库操作,然后开始根据条件每个任务进行调度。其中eligibleWorkSpecs返回的是在ENQUEUED状态下,未被执行且未被取消的WorkSpec列表,然后更新这些任务的request状态到数据库。最后遍历schedulers调用scheduler.schedule()对每个任务进行调度处理。由于示例代码创建的是没有约束的一次性任务,所以看一下GreedyScheduler对于schedule()方法的实现:

//GreedyScheduler

@Override

public void schedule(@NonNull WorkSpec... workSpecs) {

if (mIsMainProcess == null) {

// The default process name is the package name.

mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());

}

if (!mIsMainProcess) {

Logger.get().info(TAG, "Ignoring schedule request in non-main 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.

List constrainedWorkSpecs = new ArrayList<>();

List constrainedWorkSpecIds = new ArrayList<>();

for (WorkSpec workSpec : workSpecs) {

if (workSpec.state == WorkInfo.State.ENQUEUED

&& !workSpec.isPeriodic()

&& workSpec.initialDelay == 0L

&& !workSpec.isBackedOff()) {

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));

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);

}

}

}

在: (1) WorkSpec是ENQUEUED的状态 (2) 非周期性任务 (3) 非延迟任务 (4) 非撤销的任务 (5) 没有其它约束的任务 满足这五个条件后,直接调用:

//GreedyScheduler

mWorkManagerImpl.startWork(workSpec.id);

让WorkManager直接去执行任务。继续看startWork()的实现:

//WorkManagerImpl

/**

  • @param workSpecId The {@link WorkSpec} id to start
  • @hide
    */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public void startWork(@NonNull String workSpecId) {
    startWork(workSpecId, null);
    }

//WorkManagerImpl

/**

  • @param workSpecId The {@link WorkSpec} id to start
  • @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} associated with this work
  • @hide
    */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public void startWork(
    @NonNull String workSpecId,
    @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    mWorkTaskExecutor
    .executeOnBackgroundThread(
    new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }

WorkTaskExecutor对任务进行了调度。StartWorkRunnable的run()的实现:

//StartWorkRunnable

@Override

public void run() {

mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);

}

StartWorkRunnable会将任务的信息交给Processor,由Processor调用startWork()去执行任务:

//Processor

/**

  • Starts a given unit of work in the background.
  • @param id The work id to execute.
  • @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} for this work, if any.
  • @return {@code true} if the work was successfully enqueued for processing
    */
    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 (mEnqueuedWorkMap.containsKey(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 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;

}

startWork()方法中创建了一个WorkerWrapper的Runnable对象,交由WorkTaskExecutor调度处理。WorkerWrapper的run()方法的实现:

//WorkerWrapper

@WorkerThread

@Override

public void run() {

mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);

mWorkDescription = createWorkDescription(mTags);

runWorker();

}

//WorkerWrapper

private void runWorker() {

if (tryCheckForInterruptionAndResolve()) {

return;

}

mWorkDatabase.beginTransaction();

try {

mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);

...

mWorkDatabase.setTransactionSuccessful();

} finally {

mWorkDatabase.endTransaction();

}

// Merge inputs. This can be potentially expensive code, so this should not be done inside

// a database transaction.

...

WorkerParameters params = new WorkerParameters(

UUID.fromString(mWorkSpecId),

input,

mTags,

mRuntimeExtras,

mWorkSpec.runAttemptCount,

mConfiguration.getExecutor(),

mWorkTaskExecutor,

mConfiguration.getWorkerFactory(),

new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),

new WorkForegroundUpdater(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);

}

...

// 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));

mInnerFuture = mWorker.startWork();

future.setFuture(mInnerFuture);

} catch (Throwable e) {

future.setException(e);

}

}

});

// Avoid synthetic accessors.

...

}

这段代码很长,我们省略了一些判断步骤和与示例无关的参数设置。先创建一个WorkerParameters对象。然后调用mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback()方法创建Worker对象。

这个方法我们不展开了,返回的就是我们自己的Woker对象,即Worker1的实例。之后交由WorkTaskExecutor调度处理。在run()方法的实现,我们看到调用了mWorker.startWork()方法:

//ListenableWorker @MainThread public abstract @NonNull ListenableFuture startWork();

ListenableWorker是个抽象类,是所有Worker的父类。Worker1也继承Worker类,startWork()在Worker中的实现:

//Worker

@Override

public final @NonNull ListenableFuture startWork() {

mFuture = SettableFuture.create();

getBackgroundExecutor().execute(new Runnable() {

@Override

public void run() {

try {

Result result = doWork();

mFuture.set(result);

} catch (Throwable throwable) {

mFuture.setException(throwable);

}

}

});

return mFuture;

}

在run()的实现执行了doWork()方法,即执行了我们Worker1的doWork()方法。

//Worker1

class Worker1(appContext: Context, workerParams: WorkerParameters) :

Worker(appContext, workerParams) {

override fun doWork(): Result {

Thread.sleep(5000)

return Result.success()

}

}

在执行完这个任务后,返回了success。Worker也就执行完成了。回到WorkerWrapper的runWorker()方法看接下来的处理:

//WorkerWrapper

private void runWorker() {

...

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));

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();

}

}

startWork()返回了一个Future对象mInnerFuture,调用future.setFuture(mInnerFuture)去处理doWork()返回的result。再经过一系列判断后,最终执行了onWorkFinished()方法:

//WorkerWrapper

void onWorkFinished() {

boolean isWorkFinished = false;

if (!tryCheckForInterruptionAndResolve()) {

mWorkDatabase.beginTransaction();

try {

WorkInfo.State state = mWorkSpecDao.getState(mWorkSpecId);

mWorkDatabase.workProgressDao().delete(mWorkSpecId);

if (state == null) {

// state can be null here with a REPLACE on beginUniqueWork().

// Treat it as a failure, and rescheduleAndResolve() will

// turn into a no-op. We still need to notify potential observers

// holding on to wake locks on our behalf.

resolve(false);

isWorkFinished = true;

} else if (state == RUNNING) {

handleResult(mResult);

// Update state after a call to handleResult()

state = mWorkSpecDao.getState(mWorkSpecId);

isWorkFinished = state.isFinished();

} else if (!state.isFinished()) {

rescheduleAndResolve();

}

mWorkDatabase.setTransactionSuccessful();

} finally {

mWorkDatabase.endTransaction();

}

}

// Try to schedule any newly-unblocked workers, and workers requiring rescheduling (such as

// periodic work using AlarmManager). This code runs after runWorker() because it should

// happen in its own transaction.

// Cancel this work in other schedulers. For example, if this work was

// completed by GreedyScheduler, we should make sure JobScheduler is informed

// that it should remove this job and AlarmManager should remove all related alarms.

if (mSchedulers != null) {

if (isWorkFinished) {

for (Scheduler scheduler : mSchedulers) {

scheduler.cancel(mWorkSpecId);

}

}

Schedulers.schedule(mConfiguration, mWorkDatabase, mSchedulers);

}

}

在onWorkFinished()会对刚刚执行完毕的任务作进一步处理。首先获取任务的当前状态state,然后从db中删除这个任务,再根据state作进一步处理。在我们的示例中,这时候state应该是RUNNING,我们看一下handleResult(mResult)的实现:

//WorkerWrapper

private void handleResult(ListenableWorker.Result result) {

if (result instanceof ListenableWorker.Result.Success) {

Logger.get().info(

TAG,

String.format("Worker result SUCCESS for %s", mWorkDescription));

if (mWorkSpec.isPeriodic()) {

resetPeriodicAndResolve();

} else {

setSucceededAndResolve();

}

} else if (result instanceof ListenableWorker.Result.Retry) {

Logger.get().info(

TAG,

String.format("Worker result RETRY for %s", mWorkDescription));

rescheduleAndResolve();

} else {

Logger.get().info(

TAG,

String.format("Worker result FAILURE for %s", mWorkDescription));

if (mWorkSpec.isPeriodic()) {

resetPeriodicAndResolve();

} else {

setFailedAndResolve();

}

}

}

在handleResult()方法中会根据任务类型和result结果进行不同的处理。例如周期性的任务会重新将这个任务的状态设置为ENQUEUED,更新其他相关参数,并更新数据库。我们示例中已经完成的一次性任务将会被更新成SUCCEEDED的状态,具体的处理的过程就不展开了。handleResult()执行完毕后更新isWorkFinished。如果isWorkFinished为true,由于我们在GreedyScheduler已经处理了这个任务,为了避免这个任务被其他schedulers处理,WorkManager遍历了mSchedulers列表,并将这个任务从其他schedulers中移除。最后再次执行Schedulers.schedule()方法,schedule下一个任务。

总结

  1. 在WorkManager执行了enqueue()后,创建WorkContinuationImpl对象执行enqueue()方法。
  2. WorkContinuationImpl持有的EnqueueRunnable对象将任务添加到db,并交给Schedulers去调度。
  3. Schedulers将任务交给每一个Scheduler去处理。在我们的示例中,GreedyScheduler会先处理这个任务。
  4. GreedyScheduler经过一系列判断后,调用WorkManager的startWork()方法执行这种一次性,非延迟,无约束的任务。
  5. WorkManager持有的StartWorkRunnable对象会将任务交给Processor去处理,执行startWork()方法。
  6. Processor创建一个WorkerWrapper对象,由它去调用Worker的startWork()方法,执行我们自定义worker的任务,并返回相应的result。
  7. 任务完成后,WorkerWrapper会根据result对任务状态,db等进行更新,然后schedule下一个任务。

WorkManager任务执行流程图:

4.3带约束的任务的执行过程

上面的章节我们分析了一个没有约束条件的一次性任务的执行过程。接下来我们来分析一个带约束条件的任务的执行过程。

创建一个非低电量才能执行的任务:

val constraints = Constraints.Builder()

.setRequiresBatteryNotLow(true)

.build()

val work2Request = OneTimeWorkRequestBuilder()

.setConstraints(constraints)

.build()

WorkManager.getInstance(this).enqueue(work2Request)

任务的创建过程中,会为WorkSpec添加Constraints属性。

public final @NonNull B setConstraints(@NonNull Constraints constraints) {

mWorkSpec.constraints = constraints;

return getThis();

}

在任务执行的过程中,由于增加了约束条件,根据之前章节的分析,常驻的GreedyScheduler的schedule()方法将不会startWork(),而是根据build version交由SystemJobScheduler或SystemAlarmScheduler进行处理。先来看使用SystemJobScheduler的情况:

4.3.1.SystemJobScheduler(Build Version >=23)

SystemJobScheduler使用的是JobScheduler来调度执行任务。由于JobScheduler的实现过程分析不在本文的讨论范围,所以只看WorkManager是如何使用JobScheduler进行任务调度的。通常JobScheduler的使用步骤如下:

  1. 创建JobService。
  2. 配置JobInfo。
  3. 执行。

SystemJobService: SystemJobService是执行任务的服务类,在onStartJob()中,会调用WorkManagerImpl的startWork()执行任务。

//SystemJobService

@Override

public boolean onStartJob(@NonNull JobParameters params) {

... ...

String workSpecId = getWorkSpecIdFromJobParameters(params);

... ...

synchronized (mJobParameters) {

... ...

mJobParameters.put(workSpecId, params);

}

... ...

mWorkManagerImpl.startWork(workSpecId, runtimeExtras);

return true;

}

SystemJobScheduler: 在初始化SystemJobScheduler的时候会获取JobScheduler对象:

//SystemJobScheduler

public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {

this(context,

workManager,

(JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),

new SystemJobInfoConverter(context));

}

SystemJobScheduler的schedule()方法执行了scheduleInternal():

//SystemJobScheduler

public void scheduleInternal(WorkSpec workSpec, int jobId) {

JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);

Logger.get().debug(

TAG,

String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));

try {

mJobScheduler.schedule(jobInfo);

} catch (IllegalStateException e) {

... ...

throw new IllegalStateException(message, e);

} catch (Throwable throwable) {

// OEM implementation bugs in JobScheduler cause the app to crash. Avoid crashing.

Logger.get().error(TAG, String.format("Unable to schedule %s", workSpec), throwable);

}

}

SystemJobInfoConverter.convert()方法就是创建了一个JobInfo,并将Constraints里的约束条件赋予JobInfo对象,之后便执行了JobScheduler.schedule(),根据约束条件对任务进行调度。

4.3.2. SystemAlarmScheduler(Build Version <23)

SystemAlarmScheduler使用的是AlarmManager来调度执行任务。由于AlarmManager的实现过程分析不在本文的讨论范围,所以只看WorkManager是如何使用AlarmManager进行任务调度的。反编译apk后,在AndroidManifest里有如下receiver注册:

在电量变化时,收到BATTERY_LOW的广播。在BatteryNotLowProxy的onReceive()进行处理:

//ConstraintProxy

public static class BatteryNotLowProxy extends ConstraintProxy {

}

@Override

public void onReceive(Context context, Intent intent) {

Logger.get().debug(TAG, String.format("onReceive : %s", intent));

Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);

context.startService(constraintChangedIntent);

}

createConstraintsChangedIntent()的执行如下:

//ConstraintProxy

static Intent createConstraintsChangedIntent(@NonNull Context context) {

Intent intent = new Intent(context, SystemAlarmService.class);

intent.setAction(ACTION_CONSTRAINTS_CHANGED);

return intent;

}

SystemAlarmService的onStartCommand()处理如下:

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

super.onStartCommand(intent, flags, startId);

... ...

if (intent != null) {

mDispatcher.add(intent, startId);

}

// If the service were to crash, we want all unacknowledged Intents to get redelivered.

return Service.START_REDELIVER_INTENT;

}

调用了SystemAlarmDispatcher.add()方法。

//SystemAlarmDispatcher

@MainThread

public boolean add(@NonNull final Intent intent, final int startId) {

... ...

if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)

&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {

return false;

}

intent.putExtra(KEY_START_ID, startId);

synchronized (mIntents) {

boolean hasCommands = !mIntents.isEmpty();

mIntents.add(intent);

if (!hasCommands) {

// Only call processCommand if this is the first command.

// The call to dequeueAndCheckForCompletion will process the remaining commands

// in the order that they were added.

processCommand();

}

}

return true;

}

add()方法中执行了processCommand(),这段代码的核心执行语句是:

//SystemAlarmDispatcher

mCommandHandler.onHandleIntent(mCurrentIntent, startId,

SystemAlarmDispatcher.this);

在CommandHandler的onHandleIntent()方法中,action为ACTION_CONSTRAINTS_CHANGED的执行是:

//CommandHandler

if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {

handleConstraintsChanged(intent, startId, dispatcher);

}

//CommandHandler

private void handleConstraintsChanged(

@NonNull Intent intent, int startId,

@NonNull SystemAlarmDispatcher dispatcher) {

Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));

// Constraints changed command handler is synchronous. No cleanup

// is necessary.

ConstraintsCommandHandler changedCommandHandler =

new ConstraintsCommandHandler(mContext, startId, dispatcher);

changedCommandHandler.handleConstraintsChanged();

}

在handleConstraintsChanged()方法的执行中,会创建一个action为ACTION_DELAY_MET的Intent然后由SystemAlarmDispatcher发送出去,实际上也是调用了SystemAlarmDispatcher.add()方法。回到SystemAlarmDispatcher的add()流程。

//ConstraintsCommandHandler

Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);

Logger.get().debug(TAG, String.format(

"Creating a delay_met command for workSpec with id (%s)", workSpecId));

mDispatcher.postOnMainThread(

new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));

回到onHandleIntent()方法,在CommandHandler的onHandleIntent()方法中,action为ACTION_DELAY_MET的执行是:

//CommandHandler

else if (ACTION_DELAY_MET.equals(action)) {

handleDelayMet(intent, startId, dispatcher);

}

handleDelayMet()的执行过程,会调用DelayMetCommandHandler的handleProcessWork()方法,接着执行onAllConstraintsMet():

@Override

public void onAllConstraintsMet(@NonNull List workSpecIds) {

... ...

synchronized (mLock) {

if (mCurrentState == STATE_INITIAL) {

... ...

boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);

... ...

} else {

Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));

}

}

}

到这里终于看到由SystemAlarmDispatcher调用了Processor的startWork()方法,回到了之前章节分析的任务执行流程。到此为止,一个任务在不同条件下的创建,执行流程就分析完毕。

05 结语

WorkManager的使用方法简单,但是在使用时还是要分清场景,适用于可延迟,周期性,必须执行完成的任务。通过对源码的分析,WorkManager会针对不同Android版本的选择适当的策略。细致阅读代码,会发现针对指定的系统版本还有一些小的优化点。WorkManager目前已经比较稳定,所以如果在场景适合的情况下,推荐使用WorkManager来代替原有的任务管理方案。

06 参考文献

[1]https://developer.android.google.cn/topic/libraries/architecture/workmanager?

[2]https://developer.android.google.cn/preview/behavior-changes-11

07 最后

**千里之行始于足下。**Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

我把自己这段时间整理的Android最重要最热门的学习方向资料放在了**我的GitHub:https://github.com/xieyuliang/Android**,里面还有不同方向的自学编程路线、面试题集合/面经、及系列技术文章等。

资源持续更新中,欢迎大家一起学习和探讨。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

ger会针对不同Android版本的选择适当的策略。细致阅读代码,会发现针对指定的系统版本还有一些小的优化点。WorkManager目前已经比较稳定,所以如果在场景适合的情况下,推荐使用WorkManager来代替原有的任务管理方案。

06 参考文献

[1]https://developer.android.google.cn/topic/libraries/architecture/workmanager?

[2]https://developer.android.google.cn/preview/behavior-changes-11

07 最后

**千里之行始于足下。**Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

我把自己这段时间整理的Android最重要最热门的学习方向资料放在了**我的GitHub:https://github.com/xieyuliang/Android**,里面还有不同方向的自学编程路线、面试题集合/面经、及系列技术文章等。

资源持续更新中,欢迎大家一起学习和探讨。

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中...(img-4JpOcppu-1719099732554)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

相关推荐
数据发现27 分钟前
昆虫学(书籍学习资料)
数据库·数据挖掘·数据分析
码农郁郁久居人下1 小时前
Linux:文件系统与日志分析
linux·运维·服务器
-无-为-3 小时前
科普文:Linux服务器性能调优概叙
linux·运维·服务器
虫小宝3 小时前
如何在Java中实现批量数据处理
java·开发语言
king888866663 小时前
Java中的AQS
java
冰暮流星3 小时前
软设之类的继承与泛化,多重继承
java·开发语言
虫小宝3 小时前
Java中的多线程与并发编程详解
java·开发语言
oNuoyi3 小时前
定位线上同步锁仍然重复扣费的Bug定位及Redis分布式锁解决方案
java·spring boot·redis·分布式
Easonmax3 小时前
【C++】 解决 C++ 语言报错:Undefined Reference
java·开发语言·c++