WorkManager

一 使用入门
我们先了解WorkManager工作的流程:

  1. 谁来做? 定义一个负责工作的Worker
public class SimpleWorker extends Worker {
@NonNull
    @Override
    public Result doWork() {
        // return Result.failure(); // 执行失败
        // return Result.retry();   // 重试 
        // return Result.success(); // 执行成功
        return null;                // 执行结束?
    }
}

Worker是WorkManager最终实现任务的工人,它不用管会在什么实际执行任务,被安排怎样执行任务,只管处理任务doWork并根据任务执行情况反馈任务结果return Result。
Worker执行结果Result:表示任务状态,他一般有failure、retry、success三个状态,且failure、success可以携带数据Data。

2.怎么做 ?定义制定工作方案的WorkRequest
这里就有两种request,一种是执行一次的,一种是重复执行的。
非重复性工作: 工人应该一次性把工作做掉
OneTimeWorkRequest workRequest1 = OneTimeWorkRequest.from(SimpleWorker.class);
周期任务: 工人应该每隔多久做一次工作
PeriodicWorkRequest request = new PeriodicWorkRequest
.Builder(SimpleWorker.class, 16, TimeUnit.MINUTES).build();
注意: 关于周期任务有一个霸王条款:
注意:可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同)。

2.1 WorkRequest 目前有两个子类,分别为单次执行任务 OneTimeWorkRequest和周期执行任务 PeriodicWorkRequest. 他们的主要区别是任务执行方案不同导致的状态变化不同。

[简单单次执行任务 OneTimeWorkRequest 工作状态图]

avatar

对于 one-time 工作请求,工作的初始状态为 ENQUEUED。
在 ENQUEUED 状态下,您的工作会在满足其 Constraints 和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING 状态,然后可能会根据工作的结果转为 SUCCEEDED、FAILED 状态;或者,如果结果是 retry,它可能会回到 ENQUEUED 状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED 状态。

周期执行任务 PeriodicWorkRequest 工作状态图


avatar

周期性工作的初始状态为 ENQUEUED,正常执行情况下,周期性工作的状态在ENQUEUED ↔RUNNING之间交替,知道任务被取消时,状态为CANCELLED。

成功和失败状态仅适用于一次性工作和链式工作。定期工作只有一个终止状态 CANCELLED。这是因为定期工作永远不会结束。每次运行后,无论结果如何,系统都会重新对其进行调度。

上面讲了一些简单工作执行。下来看看他还有什么可以操作的地方。
二 任务监听
务进度)

上面介绍了工作的状态,我们该如何监听这些状态呢?

在将工作加入队列后,您可以随时按其 name、id 或与其关联的 tag 在 WorkManager 中进行查询,以检查其状态

// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture>

该查询会返回 WorkInfo对象的 ListenableFuture,该值包含工作的 id、其标记、其当前的 State 以及通过 Result.success(outputData)设置的任何输出数据。

利用每个方法的 LiveData变种,您可以通过注册监听器来观察 WorkInfo 的变化。例如,如果您想要在某项工作成功完成后向用户显示消息,您可以进行如下设置:


workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

复杂的工作查询

WorkManager 2.4.0 及更高版本支持使用 WorkQuery对象对已加入队列的作业进行复杂查询。WorkQuery 支持按工作的标记、状态和唯一工作名称的组合进行查询。

以下示例说明了如何查找带有“syncTag”标记、处于 FAILEDCANCELLED 状态,且唯一工作名称为“preProcess”或“sync”的所有工作。


WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture> workInfos = workManager.getWorkInfos(workQuery);

取消和停止工作

如果您不再需要运行先前加入队列的工作,则可以要求将其取消。您可以按工作的 name、id 或与其关联的 tag 取消工作。

// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

传递数据

在一些业务场景下,我们需要向任务中传递数据,可以使用androidx.work.Data向WorkRequest中添加数据。

// 创建需传递的数据
Data data = new Data.Builder().putString("message", "MainActivity").build();
// 单次执行任务
OneTimeWorkRequest request1 = new OneTimeWorkRequest.Builder(Task4DataWorker.class)
        // 向WorkRequest中添加数据
        .setInputData(data).build();
// 将任务加入队列
WorkManager.getInstance(this).enqueue(request1);

需要注意的是,Data源码可知,此处传递数据仅支持基础数据类型及其封装、String以及上述类型的数组

有传递数据自然有获取数据,我们可以Worker中通过getInputData()获取
传入的Data对象,通过Data获取传入的值,也可以通过Data和Result将值传递给观察者。

public class DataWorker extends Worker {

    public DataWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @SuppressLint("RestrictedApi")
    @NonNull
    @Override
    public Result doWork() {
        String message = getInputData().getString("message");
        Data myMsg = new Data.Builder().putString("message", "message from Worker").build();
        return new Result.Success(myMsg);
    }
}

而从Worker中传出的Data则可在WorkRequest的工作状态WorkInfo中取得:

WorkManager.getInstance(context)
               .getWorkInfoByIdLiveData(workRequest.getId())
               .observe(context, workInfo -> {
                   if (workInfo!=null){
                       if (workInfo.getState().isFinished()) {
                           String message = workInfo.getOutputData().getString("message");
                       }
                   }
               });

多任务串联

除一般的单一任务的场景外,我们面对的业务往往也要处理多个任务的场景,此时我们可以串联多个任务,也可以将多个任务编组成任务链去使用:

WorkManager.getInstance(this).beginWith(workRequest2)
                .then(workRequest3)
                .then(workRequest4)
                .enqueue();
// or
WorkContinuation continuation = WorkManager.getInstance(this)
                .beginWith(workRequest2)
                .then(workRequest3)
                .then(workRequest4);

continuation.then(workRequest).enqueue();

唯一任务

唯一工作是一个很实用的概念,可确保同一时刻只有一个具有特定名称的工作实例。与 ID 不同的是,唯一名称是人类可读的,由开发者指定,而不是由 WorkManager 自动生成。与标记不同,唯一名称仅与一个工作实例相关联。

唯一工作既可用于一次性工作,也可用于定期工作。您可以通过调用以下方法之一创建唯一工作序列,具体取决于您是调度重复工作还是一次性工作。WorkManager.enqueueUniqueWork()(用于一次性工作)
WorkManager.enqueueUniquePeriodicWork()(用于定期工作)

这两种方法都接受 3 个参数:

uniqueWorkName - 用于唯一标识工作请求的 String。
existingWorkPolicy - 此 enum 可告知 WorkManager:如果已有使用该名称且尚未完成的唯一工作链,应执行什么操作。

OneTimeWorkRequest workRequest2 = new OneTimeWorkRequest.Builder(SimpleWorker2.class).build();

WorkManager.getInstance(this).beginUniqueWork("SimpleWorker",
        ExistingWorkPolicy.REPLACE, workRequest)
//      ExistingWorkPolicy.APPEND, workRequest)
//      ExistingWorkPolicy.KEEP, workRequest)
        .then(workRequest2)
//      .then(workRequest)
        .enqueue();

此处定义的唯一,仅在任务正在执行且出现相同uniqueWorkName名称时,existingWorkPolicy才生效,无法影响已结束的同名任务(此同名仅与uniqueWorkName有关)。
以定义两个相同uniqueWorkName的WorkRequest为例,来观察existingWorkPolicy值的作用及影响

任务约束

Constraints可确保将工作延迟到满足最佳条件时运行。以下约束适用于 WorkManager。

NetworkType 约束运行工作所需的网络类型。例如 Wi-Fi (UNMETERED)。
BatteryNotLow 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。
DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果您要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议您使用此约束。
StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行

任务延时

如果工作没有约束,或者当工作加入队列时所有约束都得到了满足,那么系统可能会选择立即运行该工作。如果您不希望工作立即运行,可以将工作指定为在经过一段最短初始延迟时间后再启动。

重试和退避政策

若Worker返回结果Result.retry()时,触发重试退避政策,即下次调度Worker应在多长时间以后,支持设置退避时间基数和基数递增方式,递增方式目前支持线性LINEAR和指数EXPONENTIAL

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(RetryWorker.class)
                .setBackoffCriteria(BackoffPolicy.LINEAR,
                        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                        TimeUnit.MILLISECONDS)
                .build();

注意:退避延迟时间不精确,在两次重试之间可能会有几秒钟的差异,但绝不会低于配置中指定的初始退避延迟时间。

最短退避延迟时间设置为允许的最小值,即 10 秒。由于政策为 LINEAR,每次尝试重试时,重试间隔都会增加约 10 秒。例如,第一次运行以 Result.retry() 结束并在 10 秒后重试;然后,如果工作在后续尝试后继续返回 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退避政策设置为 EXPONENTIAL,那么重试时长序列将接近 20、40、80 秒,以此类推。
你可以为WorkRequest添加tag,从而使得你可以通过WorkManager.getWorkInfosByTag(String)获取WorkRequest的工作状态WorkInfo,你也可以直接通过WorkManager.cancelAllWorkByTag(String)取消对应标记的所有WorkRequest.

标记工作

由WorkRequest中关于tag的定义Set mTags可知,你可以为WorkRequest定义多个标记,当然的,你也可以为多个WorkRequest定义同一个标记用以统一管理。

WorkRequest request =
       new OneTimeWorkRequest.Builder(SimpleWorker.class)
       .addTag("TAG")
       .build();
// 根据tag取消任务
WorkManager.getInstance(this).cancelAllWorkByTag("TAG");
// 根据tag查找任务状态
WorkManager.getInstance(this).getWorkInfosByTag("TAG");

取消工作

// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");

注意事项

这里我们总结一下使用WorkManager的一些小Tips。

PeriodicWorkRequest周期任务可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同)
延迟工作:执行工作器的确切时间还取决于 WorkRequest 中使用的约束和系统优化方式。WorkManager 经过设计,能够在满足这些约束的情况下提供可能的最佳行为。
退避延迟时间不精确,在两次重试之间可能会有几秒钟的差异,但绝不会低于配置中指定的初始退避延迟时间。
cancelAllWorkByTag(String) 会取消具有给定标记的所有工作。
WorkRequest保证一定执行,但不保证一定在什么时间执行。

WorkManager中Service组件之一的SystemAlarmService。

SystemAlarmService 主要逻辑:


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (mIsShutdown) {
            Logger.get().info(TAG,
                    "Re-initializing SystemAlarmDispatcher after a request to shut-down.");

            // Destroy the old dispatcher to complete it's lifecycle.
            mDispatcher.onDestroy();
            // Create a new dispatcher to setup a new lifecycle.
            initializeDispatcher();
            // Set mIsShutdown to false, to correctly accept new commands.
            mIsShutdown = false;
        }

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

我们看到, 此服务在创建时初始化并绑定闹钟事件调度器,当服务被start时,若有intent事件,则将intent传递给调度器. 调度器收到intent后执行以下操作:

  @MainThread
    public boolean add(@NonNull final Intent intent, final int startId) {
        Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
        assertMainThread();
        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            Logger.get().warning(TAG, "Unknown command. Ignoring");
            return false;
        }

        // If we have a constraints changed intent in the queue don't add a second one. We are
        // treating this intent as special because every time a worker with constraints is complete
        // it kicks off an update for constraint proxies.
        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;
    }

当闹钟调度器收到新命令,会将新命令放入命令集合,若新命令为集合中第一个命令则直接进入执行命令逻辑中,否则不处理,前一命令执行完毕后再次调用执行命令:

@MainThread
private void processCommand() {
    // do something
    mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
        // do something
     try {   
        // 若当前命令不为空,则将命令交给状态机执行
        mCommandHandler.onHandleIntent(mCurrentIntent, startId, SystemAlarmDispatcher.this);
     } catch (Throwable throwable) {}
     finally {
        // 当前命令执行完毕,回调SystemAlarmDispatcher处理命令集以及后续操作
        postOnMainThread( new 
(SystemAlarmDispatcher.this));
     }
    }
}

当processCommand被调用,将开启新的线程处理命令,主要干了两件事:

将命令交给CommandHandler分发[规划任务、重新规划任务、约束条件改变、执行完毕…]事件
将事件处理完毕回调到闹钟事件调度器,由调度器处理后续事宜。
我们来依次跟进这两件事:

@WorkerThread
void onHandleIntent(@NonNull Intent intent,int startId, @NonNull SystemAlarmDispatcher dispatcher) {

    String action = intent.getAction();

    if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
        // 约束条件改变
        handleConstraintsChanged(intent, startId, dispatcher);
    } else if (ACTION_RESCHEDULE.equals(action)) {
        // 异常中断,需重新规划
        handleReschedule(intent, startId, dispatcher);
    } else {
        Bundle extras = intent.getExtras();
        if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
            // log error
        } else {
            if (ACTION_SCHEDULE_WORK.equals(action)) {
                // 规划任务
                handleScheduleWorkIntent(intent, startId, dispatcher);
            } else if (ACTION_DELAY_MET.equals(action)) {
                // 时延到点
                handleDelayMet(intent, startId, dispatcher);
            } else if (ACTION_STOP_WORK.equals(action)) {
                // 取消任务
                handleStopWork(intent, dispatcher);
            } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                // 完成任务
                handleExecutionCompleted(intent, startId);
            } else {
               // log error
            }
        }
    }
}

我们看到这里主要依据命令(intent)的action执行命令分发操作,此处我们主要关注handleDelayMet 时延结束后,处理任务的主线:

private void handleDelayMet(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {

    Bundle extras = intent.getExtras();
    // 保证任务处理线程安全
    synchronized (mLock) {
        String workSpecId = extras.getString(KEY_WORKSPEC_ID);

        // Check to see if we are already handling an ACTION_DELAY_MET for the WorkSpec.
        // If we are, then there is nothing for us to do.
        // 检查该任务是否已经在即将执行队伍(map)中,如果是则不再重复操作
        if (!mPendingDelayMet.containsKey(workSpecId)) {
            DelayMetCommandHandler delayMetCommandHandler = new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
            // 将任务放入即将执行队伍(map)中
            mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
            // 处理任务
            delayMetCommandHandler.handleProcessWork();
        } else {
            // log error
        }
    }
}

我们可以看到,CommandHandler中维护了一个 mPendingDelayMet的Map来保证单次任务不被多次处理,若命令未被处理过,则将其加入到队伍中,然后调用delayMetCommandHandler.handleProcessWork()去处理任务:

@WorkerThread
void handleProcessWork() {
    // 创建WakeLocks
    mWakeLock = WakeLocks.newWakeLock(...);
    // 获取WorkSpec
    WorkSpec workSpec = mDispatcher.getWorkManager().getWorkDatabase().workSpecDao().getWorkSpec(mWorkSpecId);
    // 处理通常不会发生的情况 - 未获取到WorkSpec。
    // 取消工作应该删除alarm,但是如果alarm已经触发,那么就触发一个stop work请求来删除挂起的delay met命令处理程序。
    if (workSpec == null) {
        stopWork();
        return;
    }
    // 跟踪工作任务是否有约束
    mHasConstraints = workSpec.hasConstraints();

    if (!mHasConstraints) {
        // 若无约束
        onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
    } else {
        // 允许跟踪器报告约束更改
        // 此处可见,我们可以将时延视作一种特殊的约束
        mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
    }
}

这里判断了任务状态及约束状态,依据约束状态执行任务或更新任务状态,
执行任务时:

@Override
public void onAllConstraintsMet(@NonNull List workSpecIds) {
    // 保证任务正确    
    if (!workSpecIds.contains(mWorkSpecId)) {
        return;
    }
    // 保证执行安全
    synchronized (mLock) {
        if (mCurrentState == STATE_INITIAL) {
            // 更改状态
            mCurrentState = STATE_START_REQUESTED;
            // startWork => doWork
            //这里没有使用WorkManagerImpl#startWork(),因为我们需要知道处理器是否在这里将工作排队
            boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
            // 若任务状态是轮询
            if (isEnqueued) {
               // 设置计时器以强制已进入队列的任务配额
                mDispatcher.getWorkTimer()
                        .startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
            } else {
                // 执行完毕或取消,则清除状态
                cleanUp();
            }
        } else {
            Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
        }
    }
}

这里最终改变任务状态并调用Processor.startWork()执行任务,我们将关注Processor.startWork(),这个方法最终执行到我们定义的Worker中doWork()方法,并且前面说到的WorkManager中负责任务执行的Service们最终都会调用到Processor.startWork()。

首先我们简单过一下 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)的代码:

public boolean startWork(@NonNull String id,  @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    WorkerWrapper workWrapper;
    // 保证任务队列(MAP)只进或只出
    synchronized (mLock) {
        // doing 验证id不在队列中
        // 初始化WorkerWrapper
        workWrapper = new WorkerWrapper.Builder(...).build();
        ListenableFuture future = workWrapper.getFuture();
        // 为执行结果添加监听,注意这里监听回调何时调用后面会说明
        // 特别注意:future.addListener中 FutureListener 是一个Runnable对象
        // 特别注意: FutureListener 是一个Runnable对象
        future.addListener(
                new FutureListener(this, id, future),
                mWorkTaskExecutor.getMainThreadExecutor());
        // 将此任务加入队列
        mEnqueuedWorkMap.put(id, workWrapper);
    }
    // 使用工作线程执行任务
    mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
}

mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)主要做了三个操作:

  • 将任务要素封装为WorkerWrapper并为其执行期望添加监听
  • 任务要素入队
  • 工作线程执行处理任务
    相对主要的,我们继续跟进工作线程处理任务流程execute(Runnable), excute将WorkerWrapper继续封装为Task并加入任务队列,若当前无活跃Task则安排任务执行,任务执行完毕后继续检查任务队列,直到任务队列空置为止:
@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);
        }
    }
}

mExecutor.execute(mActive)会通过Task执行execute(Runnable)传入的Runnable,即执行WorkerWrapper,Task不论WorkerWrapper执行结果最终会继续轮询任务队列。此时,我们将目光移到WorkerWrapper.run():

@WorkerThread
@Override
public void run() {
    mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
    mWorkDescription = createWorkDescription(mTags);
    runWorker();
}
private void runWorker() {
// 各个状态判断最终调用到resolve(false);
resolve(true); // OR resolve(false);

// 尝试将工作设置为运行状态。注意,这可能会失败,因为自从上次在这个函数的顶部检查之后,另一个线程可能已经修改了数据库。
if (trySetRunning()) {
    if (tryCheckForInterruptionAndResolve()) {
        return;
    }

    final SettableFuture future = SettableFuture.create();
    // Call mWorker.startWork() on the main thread.
    mWorkTaskExecutor.getMainThreadExecutor()
            .execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 调用Worker执行工作任务
                        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() {
           // 最终调用到resolve(false);
           resolve(false);
        }
    }, mWorkTaskExecutor.getBackgroundExecutor());
} else {
     // 最终调用到resolve(false);
     resolve(boolean);
}

}

private void resolve(final boolean needsReschedule) {
    // 数据库事务操作
    
    mFuture.set(needsReschedule);
}

runWorker() 中包含各种任务状态的判断,并尝试将任务置为运行状态,若设置运行状态成功,则调用Worker.startWork() 即最终会走向个人定义的Worker的doWork()方法:

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

DequeueAndCheckForCompletion如何完成回调:
DequeueAndCheckForCompletion 是一个Runnable对象,postOnMainThread时调用run:

@Override
public void run() {
    mDispatcher.dequeueAndCheckForCompletion();
}

最终回调到dequeueAndCheckForCompletion:

void dequeueAndCheckForCompletion() {
   // 保证mIntents命令集只进或只出
    synchronized (mIntents) {
        if (mCurrentIntent != null) {
            // 从命令集中移除已完成(第一个)命令
            if (!mIntents.remove(0).equals(mCurrentIntent)) {
                throw new IllegalStateException("something");
            }
            mCurrentIntent = null;
        }
        SerialExecutor serialExecutor = mTaskExecutor.getBackgroundExecutor();
        // 若无预计执行和待执行命令,则通知SystemAlarmService执行完毕
        if (!mCommandHandler.hasPendingCommands()
                && mIntents.isEmpty()
                && !serialExecutor.hasPendingTasks()) {
            if (mCompletedListener != null) {
                mCompletedListener.onAllCommandsCompleted();
            }
        } else if (!mIntents.isEmpty()) {
           // 若命令集中还有未执行的命令,则继续执行
            processCommand();
        }
    }
}

此时,若当前命令存在,则从命令集中移除当前命令。若无预计执行和待执行命令,则调用mCompletedListener.onAllCommandsCompleted()通知SystemAlarmService执行完毕;若命令集合中还有未执行命令,则调用processCommand()继续执行。

而SystemAlarmService.onAllCommandsCompleted()最终会执行stopSelf()停止服务

SystemJobService

Service invoked by {@link android.app.job.JobScheduler} to run work tasks.
服务将被JobScheduler调用来执行工作任务。
从描述可得SystemJobService与SystemAlarmService职责是一致的,都是“run work tasks”。

类似地,我们可以猜测SystemJobService执行流程始于onStartJob终于onStopJob。

SystemJobService.onCreate()获取了WorkManagerImpl实例并将执行监听绑定到(任务)处理器Processor:
// 还记得WorkManagerImpl是哪里初始化的吗?
mWorkManagerImpl = WorkManagerImpl.getInstance(getApplicationContext());
// 记住此处addExecutionListener
// 记住此处addExecutionListener
// 记住此处addExecutionListener
mWorkManagerImpl.getProcessor().addExecutionListener(this);

当SystemJobService被调用时会执行SystemJobService.onStartJob(),此时会将JobParameters以workId为Key存入工作Map中,并初始化WorkerParameters.RuntimeExtras, 最终调用mWorkManagerImpl.startWork(workSpecId, runtimeExtras)执行任务:

public boolean onStartJob(@NonNull JobParameters params) {
    // WorkManagerImpl NonNull
    String workSpecId = getWorkSpecIdFromJobParameters(params);
    // workSpecId NonNull
    // 保证mJobParameters 工作参数集只进或只出
    synchronized (mJobParameters) {
        if (mJobParameters.containsKey(workSpecId)) {
           // log
            return false;
        }
        // 存入工作参数
        mJobParameters.put(workSpecId, params);
    }
    // 初始化WorkerParameters.RuntimeExtras
    
    // WorkManagerImpl执行任务
    mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
}

WorkManagerImpl.startWork后续会执行 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)。
由Processor.startWork()中代码可知,流程末尾onExecuted中遍历执行的executionListener.onExecuted(workSpecId, needsReschedule)会调用SystemJobService.onExecuted():

@Override
public void onExecuted(@NonNull String workSpecId, boolean needsReschedule) {
    Logger.get().debug(TAG, String.format("%s executed on JobScheduler", workSpecId));
    JobParameters parameters;
    synchronized (mJobParameters) {
        parameters = mJobParameters.remove(workSpecId);
    }
    if (parameters != null) {
        jobFinished(parameters, needsReschedule);
    }
}

当然,根据JobService的生命周期,我们可以认定,最后会调用SystemJobService.onStopJob()

@Override
public boolean onStopJob(@NonNull JobParameters params) {
    if (mWorkManagerImpl == null) {
        return true;
    }

    String workSpecId = getWorkSpecIdFromJobParameters(params);
    if (TextUtils.isEmpty(workSpecId)) {
        return false;
    }
    
    // mJobParameters数据安全
    synchronized (mJobParameters) {
        // 移出任务参数队列(Map)
        mJobParameters.remove(workSpecId);
    }
    // 结束此任务
    mWorkManagerImpl.stopWork(workSpecId);
    return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId);
}

SystemForegroundService

在看过前面SystemAlarmService & SystenJobService 后,我本以为
SystemForegroundService也如前者一般是处理任务执行的,但简单阅读代码后发现并非如此,尽管结构上与前者类似,但其主要作用是给运行任务的服务提权,保证其在前台执行
SystemAlarmService & SystenJobService相似,SystemForegroundService也是LifecycleService的子类,主要方法有:

@Override
public void onCreate() {
    super.onCreate();
    sForegroundService = this;
    initializeDispatcher();
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    // 若正在被关闭,则
    if (mIsShutdown) {
        // 结束旧    Dispatcher 的生命周期
        mDispatcher.onDestroy();
        // 创建新的Dispatcher并设置新的生命周期
        initializeDispatcher();
        mIsShutdown = false;
    }
    if (intent != null) {
        // 告诉Dispatcher 有活儿来了
        mDispatcher.onStartCommand(intent);
    }
    // 如果服务崩溃,我们希望所有未确认的意图都得到重新交付。
    return Service.START_REDELIVER_INTENT;
}
@MainThread
private void initializeDispatcher() {
    mHandler = new Handler(Looper.getMainLooper());
    mNotificationManager = (NotificationManager)
            getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
    mDispatcher = new SystemForegroundDispatcher(getApplicationContext());
    mDispatcher.setCallback(this);
}

初始化时创建新的SystemForegroundDispatcher绑定并设置新的生命周期,当收到指令被启动时,调用SystemForegroundDispatcher.onStartCommand():

// SystemForegroundDispatcher#onStartCommand
void onStartCommand(@NonNull Intent intent) {
    String action = intent.getAction();
    if (ACTION_START_FOREGROUND.equals(action)) {
        handleStartForeground(intent);
        // 调用handleNotify(),它反过来调用start前台()作为处理这个命令的一部分。这对一些原始设备制造商来说很重要。
        handleNotify(intent);
    } else if (ACTION_NOTIFY.equals(action)) {
        handleNotify(intent);
    } else if (ACTION_CANCEL_WORK.equals(action)) {
        handleCancelWork(intent);
    }
}

当intent (此处intent可视作前面提到的SystemXXXService的意图) 状态处于启动、更新状态时会调用handleNotify(intent),取消时会调用handleCancelWork(intent),
状态为取消时,则最终会调用mWorkManagerImpl.cancelWorkById(UUID.fromString(workSpecId))取消任务执行。

我们再来看看handleNotify干了哪些事情吧:

// SystemForegroundDispatcher#handleNotify
@MainThread
private void handleNotify(@NonNull Intent intent) {
    int notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, 0);
    int notificationType = intent.getIntExtra(KEY_FOREGROUND_SERVICE_TYPE, 0);
    String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
    Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
    if (notification != null && mCallback != null) {
        ForegroundInfo info = new ForegroundInfo(
                notificationId, notification, notificationType);
        // 缓存信息
        mForegroundInfoById.put(workSpecId, info);
        if (TextUtils.isEmpty(mCurrentForegroundWorkSpecId)) {
            // 这是拥有前台生命周期的当前workSpecId.
            mCurrentForegroundWorkSpecId = workSpecId;
            mCallback.startForeground(notificationId, notificationType, notification);
        } else {
            // 更新通知
            mCallback.notify(notificationId, notification);
            // 如有必要,更新前台的通知,使其成为当前所有前台服务类型的联合。
            if (notificationType != FOREGROUND_SERVICE_TYPE_NONE
                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                int foregroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
                for (Map.Entry entry : mForegroundInfoById.entrySet()) {
                    ForegroundInfo foregroundInfo = entry.getValue();
                    foregroundServiceType |= foregroundInfo.getForegroundServiceType();
                }
                //缓存中取出信息
                ForegroundInfo currentInfo = mForegroundInfoById.get(mCurrentForegroundWorkSpecId);
                if (currentInfo != null) {
                    mCallback.startForeground(
                            currentInfo.getNotificationId(),
                            foregroundServiceType,
                            currentInfo.getNotification()
                    );
                }
            }
        }
    }
}

若当前服务intent是首个,那么就启动SystemForegroundService.startForeground()做周期性递归,而SystemForegroundService.startForeground()实际上是一个空方法,类似于使用了Looper保证SystemForegroundService一直运行:

// SystemForegroundService#startForeground
@Override
public void startForeground(
        final int notificationId,
        final int notificationType,
        @NonNull final Notification notification) {

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                startForeground(notificationId, notification, notificationType);
            } else {
                startForeground(notificationId, notification);
            }
        }
    });
}

若当前服务intent不是首个,则更新一则通知,并合并前台服务类型,mCallback.notify(notificationId, notification):

// SystemForegroundService#notify
@Override
public void notify(final int notificationId, @NonNull final Notification notification) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mNotificationManager.notify(notificationId, notification);
        }
    });
}

SystemForegroundService会在任务启动时启动,且会创建一个类似于Looper的结构保持服务前台持续运行,后续有任务触发时,会发送notification以保证其执行任务的服务可被视作前台服务保持运行。

广播接收者

WorkManager使用的重要组件:广播接收者,主要涉及意外停止监听广播ForceStopRunnable.BroadcastReceiver、约束状态监听广播ConstraintProxy.*、启动重新规划服务的广播RescheduleReceiver、代理约束更新广播ConstraintProxyUpdateReceiver、测试诊断广播DiagnosticsReceiver。此类组件为WorkManager稳定运行、重新规划、约束更新提供了支持.

ForceStopRunnable

WorkManager is restarted after an app was force stopped. Alarms and Jobs get cancelled when an application is force-stopped. To reschedule, we create a pending alarm that will not survive force stops.
强制停止应用程序后,WorkManager将重新启动。当应用被强制停止时,Alarms和Jobs将被取消。为了重新安排,我们要创建一个无法通过强制停止的Alarm。

在介绍ForceStopRunnable.BroadcastReceiver前,我们先简单看一下它的外部类ForceStopRunnable。
ForceStopRunnable是一个Runnable对象,核心方法是run, 其在WorkManager初始化时调用WorkManagerImpl.internalInit(), 构造并调用了ForceStopRunnable.run():

@Override
public void run() {
    // 如果需要,将数据库迁移到不备份的目录。
    WorkDatabasePathHelper.migrateDatabase(mContext);
    // 清除属于WorkManager的无效作业,以及由于应用程序崩溃(运行状态)而可能被中断的作业。
    try {
        boolean needsScheduling = cleanUp();
        if (shouldRescheduleWorkers()) {
            mWorkManager.rescheduleEligibleWork();
            // 将WorkManager标记为已迁移。
            mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
        } else if (isForceStopped()) {
            // 异常退出,重新规划任务
            mWorkManager.rescheduleEligibleWork();
        } else if (needsScheduling) {
           // 发现未完成任务,规划它们
            Schedulers.schedule(
                    mWorkManager.getConfiguration(),
                    mWorkManager.getWorkDatabase(),
                    mWorkManager.getSchedulers());
        }
        mWorkManager.onForceStopRunnableCompleted();
    } catch (SQLiteCantOpenDatabaseException
            | SQLiteDatabaseCorruptException
            | SQLiteAccessPermException exception) {
        // ForceStopRunnable通常是访问数据库(或应用程序的内部数据目录)的第一件事。
        // 这意味着奇怪的PackageManager错误被归咎于ForceStopRunnable,这是不幸的。
        // 这为开发人员提供了更好的错误消息。
        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);
    }
}

我们发现ForceStopRunable主要职责是处理异常中断后对WorkManager中任务进行清理和重新规划,run()主要干了3件事:

如果需要,将数据库迁移到不备份的目录
取消无效的JobScheduler作业/重新调度以前正在运行的作业
针对异常中断时WorkManager的状态进行不同的调度操作
那么他是如何判断是否是强制中断的呢,我们可以看下强制中断判断代码isForceStopped():

@VisibleForTesting
public boolean isForceStopped() {
    // 当应用程序在Eclair MR1强制停止启动时,Alarm被取消。
    // 在N-MR1中引入了强制停止作业的取消(SDK 25)。
    // 尽管API 23、24可能是安全的,但oem可能会选择采用不同的方法。
    PendingIntent pendingIntent = getPendingIntent(mContext, FLAG_NO_CREATE);
    if (pendingIntent == null) {
        setAlarm(mContext);
        return true;
    } else {
        return false;
    }
}

通过判断闹钟广播是否存在来确定应用是否被强行停止,若不存在即闹钟Alarm被取消即是强制中断,此时将重新设置一个闹钟Alarm;

让我们来看看上文中的ForceStopRunnable.BroadcastReceiver闹钟广播。
BroadcastReceiver

A {@link android.content.BroadcastReceiver} which takes care of recreating the long lived alarm which helps track force stops for an application. This is the target of the alarm set by ForceStopRunnable in {@link #setAlarm(Context)}. 一个{@link android.content.BroadcastReceiver}负责重新创建长寿命的Alarm,这有助于跟踪应用程序的强制停止。这是在{@link #setAlarm(Context)}中由forceoprunnable设置的Alarm目标。

从描述可知,此广播的主要目的是负责创建长期存活的闹钟,用以追踪应用程序的强制停止。

其代码相对简单,若收到action为ACTION_FORCE_STOP_RESCHEDULE的广播,则设置一个长达十年的可唤醒设备的闹钟,然后再来一次(发送action为ACTION_FORCE_STOP_RESCHEDULE的广播)。

// ForceStopRunnable

private static PendingIntent getPendingIntent(Context context, int flags) {
    Intent intent = getIntent(context);
    // 发送广播的PendingIntent
    return PendingIntent.getBroadcast(context, ALARM_ID, intent, flags);
}

@VisibleForTesting
static Intent getIntent(Context context) {
    Intent intent = new Intent();
    intent.setComponent(new ComponentName(context, ForceStopRunnable.BroadcastReceiver.class));
    // 广播intent
    intent.setAction(ACTION_FORCE_STOP_RESCHEDULE);
    return intent;
}

static void setAlarm(Context context) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    //使用FLAG_UPDATE_CURRENT,因为我们只需要这个Alarm的一次实例
    PendingIntent pendingIntent = getPendingIntent(context, FLAG_UPDATE_CURRENT);
    // 设置执行时间为十年
    long triggerAt = System.currentTimeMillis() + TEN_YEARS;
    if (alarmManager != null) {
        if (Build.VERSION.SDK_INT >= 19) {
            // RTC_WAKEUP:绝对时间可唤醒类型,十年后,干啥
            alarmManager.setExact(RTC_WAKEUP, triggerAt, pendingIntent);
        } else {
            alarmManager.set(RTC_WAKEUP, triggerAt, pendingIntent);
        }
    }
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static class BroadcastReceiver extends android.content.BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Our alarm somehow got triggered, so make sure we reschedule it.  This should really never happen because we set it so far in the future.
        // 我们的闹钟不知怎么被触发了,所以一定要重新安排时间。这是不应该发生的,因为我们将它设置在遥远的未来(嘿,十年后再见)。
        if (intent != null) {
            String action = intent.getAction();
            if (ACTION_FORCE_STOP_RESCHEDULE.equals(action)) {
                Logger.get().verbose(
                        TAG,
                        "Rescheduling alarm that keeps track of force-stops.");
                ForceStopRunnable.setAlarm(context);
            }
        }
    }
}

有前文可知,应用启动时会初始化WorkManager,WorkManager初始化则会执行ForceStopRunnable.run(), 此时一般会调用setAlarm(), 创建Alarm,极端情况下会存在应用存活十年的情况,此BroadcastReceiver即是用于处理这种情况的,当十年后又收到了这个广播,那么我们在创建一个十年期的闹钟Alarm.

ConstraintProxy

BatteryNotLowProxy
BatteryChargingProxy
StorageNotLowProxy
NetworkStateProxy

上述BatteryNotLowProxy、BatteryChargingProxy、StorageNotLowProxy、NetworkStateProxy均是ConstraintProxy的子类,实现均一致,仅有注册action不同,用以针对不同action的系统广播更新约束状态, 此处一并分析之。

以NetworkStateProxy为例,当网络状态变化,应用收到"android.net.conn.CONNECTIVITY_CHANGE"广播,触发NetworkStateProxy.onReceive():

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

调用CommandHandler.createConstraintsChangedIntent(context)调起SystemAlarmService, 其intent的action为"ACTION_CONSTRAINTS_CHANGED":

static Intent createConstraintsChangedIntent(@NonNull Context context) {
    Intent intent = new Intent(context, SystemAlarmService.class);
    intent.setAction(ACTION_CONSTRAINTS_CHANGED);
    return intent;
}

最终会调用到CommandHandler.onHandleIntent():

void onHandleIntent(@NonNull Intent intent, int startId, @NonNull SystemAlarmDispatcher dispatcher) {
        String action = intent.getAction();

        if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            handleConstraintsChanged(intent, startId, dispatcher);
        } 
        // 其他
}

private void handleConstraintsChanged(
        @NonNull Intent intent, int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {
    ConstraintsCommandHandler changedCommandHandler =
            new ConstraintsCommandHandler(mContext, startId, dispatcher);
    changedCommandHandler.handleConstraintsChanged();
}

此时intent的action为"ACTION_CONSTRAINTS_CHANGED",最终会调用到ConstraintsCommandHandler.handleConstraintsChanged():

void handleConstraintsChanged() {
    List candidates = mDispatcher.getWorkManager().getWorkDatabase()
            .workSpecDao()
            .getScheduledWork();

    //更新约束代理以潜在地禁用先前完成的WorkSpecs的代理。
    ConstraintProxy.updateAll(mContext, candidates);

    // 这需要在每个约束控制器中填充匹配的WorkSpec id。标记正在更新这些工作的状态
    mWorkConstraintsTracker.replace(candidates);

    List eligibleWorkSpecs = new ArrayList<>(candidates.size());
    // 筛选候选人应该已经规划好了。
    long now = System.currentTimeMillis();
    for (WorkSpec workSpec : candidates) {
        String workSpecId = workSpec.id;
        long triggerAt = workSpec.calculateNextRunTime();
        // 时间条件符合且无约束或约束已符合,则加入符合条件集合
        if (now >= triggerAt && (!workSpec.hasConstraints()
                || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
            eligibleWorkSpecs.add(workSpec);
        }
    }

    for (WorkSpec workSpec : eligibleWorkSpecs) {
        String workSpecId = workSpec.id;
        Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
        // 告诉SystemAlarmDispatcher这个工作已经符合条件了,请给安排上
        mDispatcher.postOnMainThread(
                new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
    }
    // 重置正在更新这些工作状态的标记
    mWorkConstraintsTracker.reset();
}

此方法先获取了规划中的工作生成集合,调用了ConstraintProxy.updateAll()方法更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,遍历判断每个WorkSpec是否符合条件,遍历符合约束条件的列表,告诉SystemAlarmDispatcher添加任务,若SystemAlarmDispatcher中无任务则分发执行任务(还记得processCommand()是一个环状的轮询吗?)。

SystemAlarmDispatcher.AddRunnable()后续调用SystemAlarmDispatcher.add()在SystemAlarmService中也有
SystemAlarmDispatcher.AddRunnable() -> SystemAlarmDispatcher.add()

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

    String action = intent.getAction();
    if (TextUtils.isEmpty(action)) {
        return false;
    }

    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) {
            processCommand();
        }
    }
    return true;
}

static class AddRunnable implements Runnable {
    @Override
    public void run() {
        mDispatcher.add(mIntent, mStartId);
    }
}

ConstraintProxyUpdateReceiver
上面讲到ConstraintsCommandHandler.handleConstraintsChanged()第一步会调用ConstraintProxy.updateAll(mContext, candidates)最终会发送广播给ConstraintProxyUpdateReceiver更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,我们简单过一下。

static void updateAll(Context context, List workSpecs) {
    boolean batteryNotLowProxyEnabled = false;
    boolean batteryChargingProxyEnabled = false;
    boolean storageNotLowProxyEnabled = false;
    boolean networkStateProxyEnabled = false;
// 查找每个WorkSpec(是否有)约束条件状态
    for (WorkSpec workSpec : workSpecs) {
        Constraints constraints = workSpec.constraints;
        batteryNotLowProxyEnabled |= constraints.requiresBatteryNotLow();
        batteryChargingProxyEnabled |= constraints.requiresCharging();
        storageNotLowProxyEnabled |= constraints.requiresStorageNotLow();
        networkStateProxyEnabled |= constraints.getRequiredNetworkType() != NOT_REQUIRED;
// 知道需要全部广播接收者,就不用判断了
        if (batteryNotLowProxyEnabled && batteryChargingProxyEnabled
                && storageNotLowProxyEnabled && networkStateProxyEnabled) {
            break;
        }
    }

    Intent updateProxyIntent =
            ConstraintProxyUpdateReceiver.newConstraintProxyUpdateIntent(
                    context,
                    batteryNotLowProxyEnabled,
                    batteryChargingProxyEnabled,
                    storageNotLowProxyEnabled,
                    networkStateProxyEnabled);

    // ConstraintProxies are being updated via a separate broadcast receiver.
    // For more information on why we do this look at b/73549299
    context.sendBroadcast(updateProxyIntent);
}

这里先是判断需要哪些广播接收者,会发送广播给ConstraintProxyUpdateReceiver,最终会调用:

@Override
public void onReceive(@NonNull final Context context, @Nullable final Intent intent) {
    String action = intent != null ? intent.getAction() : null;
    if (!ACTION.equals(action)) {

    } else {
        final PendingResult pendingResult = goAsync();
        WorkManagerImpl workManager = WorkManagerImpl.getInstance(context);
        TaskExecutor taskExecutor = workManager.getWorkTaskExecutor();
        taskExecutor.executeOnBackgroundThread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 在后台线程上执行此操作,因为使用PackageManager来启用或禁用代理涉及到对文件系统的写操作。
                    boolean batteryNotLowProxyEnabled = intent.getBooleanExtra(
                            KEY_BATTERY_NOT_LOW_PROXY_ENABLED, false);
                    boolean batteryChargingProxyEnabled = intent.getBooleanExtra(
                            KEY_BATTERY_CHARGING_PROXY_ENABLED, false);
                    boolean storageNotLowProxyEnabled = intent.getBooleanExtra(
                            KEY_STORAGE_NOT_LOW_PROXY_ENABLED, false);
                    boolean networkStateProxyEnabled = intent.getBooleanExtra(
                            KEY_NETWORK_STATE_PROXY_ENABLED, false);

                    Logger.get().debug(
                            TAG,
                            String.format("Updating proxies: BatteryNotLowProxy enabled (%s), "
                                            + "BatteryChargingProxy enabled (%s), "
                                            + "StorageNotLowProxy (%s), "
                                            + "NetworkStateProxy enabled (%s)",
                                    batteryNotLowProxyEnabled,
                                    batteryChargingProxyEnabled,
                                    storageNotLowProxyEnabled,
                                    networkStateProxyEnabled));

                    PackageManagerHelper.setComponentEnabled(context, BatteryNotLowProxy.class,
                            batteryNotLowProxyEnabled);
                    PackageManagerHelper.setComponentEnabled(context,
                            BatteryChargingProxy.class,
                            batteryChargingProxyEnabled);
                    PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class,
                            storageNotLowProxyEnabled);
                    PackageManagerHelper.setComponentEnabled(context, NetworkStateProxy.class,
                            networkStateProxyEnabled);
                } finally {
                    pendingResult.finish();
                }
            }
        });
    }
}

主要执行了获取前文提供的需要哪些约束广播接收器,调用PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class, storageNotLowProxyEnabled)更改广播接收者注册信息:

/**
 * 使用{@link PackageManager} 去开/关manifest声明的组件
 *
 * @param context {@link Context}
 * @param klazz The class of component
 * @param enabled {@code true} 开关组件
 */
public static void setComponentEnabled(
        @NonNull Context context,
        @NonNull Class klazz,
        boolean enabled) {
    try {
        PackageManager packageManager = context.getPackageManager();
        ComponentName componentName = new ComponentName(context, klazz.getName());
        packageManager.setComponentEnabledSetting(componentName,
                enabled
                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);

    } catch (Exception exception) {
    }
}

RescheduleReceiver

Reschedules alarms on BOOT_COMPLETED and other similar scenarios.
在BOOT_COMPLETED和其他类似场景下重新调度Alarm。

DiagnosticsReceiver

The {@link android.content.BroadcastReceiver} which dumps out useful diagnostics information. 输出有用的诊断信息

你可能感兴趣的:(WorkManager)