一 使用入门
我们先了解WorkManager工作的流程:
- 谁来做? 定义一个负责工作的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 工作状态图]
对于 one-time 工作请求,工作的初始状态为 ENQUEUED。
在 ENQUEUED 状态下,您的工作会在满足其 Constraints 和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING 状态,然后可能会根据工作的结果转为 SUCCEEDED、FAILED 状态;或者,如果结果是 retry,它可能会回到 ENQUEUED 状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED 状态。
周期执行任务 PeriodicWorkRequest 工作状态图
周期性工作的初始状态为 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”标记、处于 FAILED
或 CANCELLED
状态,且唯一工作名称为“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. 输出有用的诊断信息