文章目录
WorkManager主要是用来处理后台任务的,并且保证这些任务可以在App退出时在适当时机执行,实现这一目标主要是依靠:
- 使用Room将任务以WorkSec为单元存储在本地;
- 使用反射将Work类实例化,并执行其中定义的任务,保证了App退出时任务依然能得到执行。
其中后台任务分为两类:
- 立即执行的任务;
- 限制的任务。
WorkManager使用及介绍参考Exploring Jetpack: Scheduling tasks with Work Manager,WorkManager的原理参考Android Jetpack之WorkManager源码分析,下面主要记录几点细节。
1. 任务的启动
开始任务是通过调用Processor类的startWork()方法实现:
//Processor.java
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
// Work may get triggered multiple times if they have passing constraints
// and new work with those constraints are added.
if (mEnqueuedWorkMap.containsKey(id)) {
return false;
}
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
return true;
}
这段代码构造了一个WorkerWrapper对象,然后获取WorkerWrapper任务执行完的结果的引用future,并对该future添加监听:
//AbstractFuture.java
@Override
public final void addListener(Runnable listener, Executor executor) {
checkNotNull(listener);
checkNotNull(executor);
Listener oldHead = listeners;
if (oldHead != Listener.TOMBSTONE) {
Listener newNode = new Listener(listener, executor);
do {
newNode.next = oldHead;
if (ATOMIC_HELPER.casListeners(this, oldHead, newNode)) {
return;
}
oldHead = listeners; // re-read
} while (oldHead != Listener.TOMBSTONE);
}
// If we get here then the Listener TOMBSTONE was set, which means the future is done, call
// the listener.
executeListener(listener, executor);
}
将一个listener构造程一个Listener对象newNode,并添加到AbstractFuture中的listeners成员变量指向的链表头部,并且将listeners指向新的newNode。在添加过程中有可能该WorkerWrapper已经执行完毕(重复添加唯一型任务),原来的监听对象oldHead会被设置为Listener.TOMBSTONE,此时while循环结束,并立即执行新添加的listener。Processor类的startWork()方法接下来就会执行该WorkerWrapper对象,其run()方法会执行:
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
if (tryCheckForInterruptionAndResolve()) {
return;
}
mWorkDatabase.beginTransaction();
//1. 执行任务条件判决
try {
//1.1 数据库中没有该mWorkSpecId则不执行
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
if (mWorkSpec == null) {
Logger.get().error(
TAG,
String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
resolve(false);
return;
}
// Do a quick check to make sure we don't need to bail out in case this work is already
// running, finished, or is blocked.
//1.2 数据库中包含该任务,但是该任务的状态不是ENQUEUED也不执行
if (mWorkSpec.state != ENQUEUED) {
resolveIncorrectStatus();
mWorkDatabase.setTransactionSuccessful();
Logger.get().debug(TAG,
String.format("%s is not in ENQUEUED state. Nothing more to do.",
mWorkSpec.workerClassName));
return;
}
// Case 1:
// Ensure that Workers that are backed off are only executed when they are supposed to.
// GreedyScheduler can schedule WorkSpecs that have already been backed off because
// it is holding on to snapshots of WorkSpecs. So WorkerWrapper needs to determine
// if the ListenableWorker is actually eligible to execute at this point in time.
// Case 2:
// On API 23, we double scheduler Workers because JobScheduler prefers batching.
// So is the Work is periodic, we only need to execute it once per interval.
// Also potential bugs in the platform may cause a Job to run more than once.
//1.3 对于定期任务和多次执行任务,除第一次外未到执行时间的也不执行
if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
long now = System.currentTimeMillis();
// Allow first run of a PeriodicWorkRequest
// to go through. This is because when periodStartTime=0;
// calculateNextRunTime() always > now.
// For more information refer to b/124274584
boolean isFirstRun = mWorkSpec.periodStartTime == 0;
if (!isFirstRun && now < mWorkSpec.calculateNextRunTime()) {
Logger.get().debug(TAG,
String.format(
"Delaying execution for %s because it is being executed "
+ "before schedule.",
mWorkSpec.workerClassName));
// For AlarmManager implementation we need to reschedule this kind of Work.
// This is not a problem for JobScheduler because we will only reschedule
// work if JobScheduler is unaware of a jobId.
resolve(true);
return;
}
}
// Needed for nested transactions, such as when we're in a dependent work request when
// using a SynchronousExecutor.
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
// Merge inputs. This can be potentially expensive code, so this should not be done inside
// a database transaction.
Data input;
if (mWorkSpec.isPeriodic()) {
//2.1 对于定期任务,不需要合并
input = mWorkSpec.input;
} else {
//2.2. 对于链式任务,需要对父任务的outpute进行合并,并做为当前任务的inpute
InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
String inputMergerClassName = mWorkSpec.inputMergerClassName;
InputMerger inputMerger =
inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
if (inputMerger == null) {
Logger.get().error(TAG, String.format("Could not create Input Merger %s",
mWorkSpec.inputMergerClassName));
setFailedAndResolve();
return;
}
List<Data> inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
}
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mForegroundProcessor, mWorkTaskExecutor));
// Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
// in test mode.
//3 用workerClassName反射创建一个Work实例
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
if (mWorker == null) {
Logger.get().error(TAG,
String.format("Could not create Worker %s", mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
if (mWorker.isUsed()) {
Logger.get().error(TAG,
String.format("Received an already-used Worker %s; WorkerFactory should return "
+ "new instances",
mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
mWorker.setUsed();
// Try to set the work to the running state. Note that this may fail because another thread
// may have modified the DB since we checked last at the top of this function.
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
// Call mWorker.startWork() on the main thread.
//4.1 在主线程开启该mWorker任务
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.
//4.2 为该mWorker的执行加过添加监听
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();
}
}
runWorker()的逻辑比较复杂,具体分析见代码注释,这里看一下"3 用workerClassName反射创建一个Work实例"的过程:
//WorkerFactory.java
public final @Nullable ListenableWorker createWorkerWithDefaultFallback(
@NonNull Context appContext,
@NonNull String workerClassName,
@NonNull WorkerParameters workerParameters) {
ListenableWorker worker = createWorker(appContext, workerClassName, workerParameters);
if (worker == null) {
// Fallback to reflection
Class<? extends ListenableWorker> clazz = null;
try {
clazz = Class.forName(workerClassName).asSubclass(ListenableWorker.class);
} catch (ClassNotFoundException e) {
Logger.get().error(TAG, "Class not found: " + workerClassName);
}
if (clazz != null) {
try {
Constructor<? extends ListenableWorker> constructor =
clazz.getDeclaredConstructor(Context.class, WorkerParameters.class);
worker = constructor.newInstance(
appContext,
workerParameters);
} catch (Exception e) {}
}
}
return worker;
}
对于“2.2 链式任务input的合并”中,初始化时指定的InputMergerFactory是默认的,其createInputMerger()方法返回null,因此这里会采用反射方式将inputMergerClassName实例化,inputMergerClassName对应的类为OverwritingInputMerger,其合并方法为:
//OverwritingInputMerger.java
@Override
public @NonNull Data merge(@NonNull List<Data> inputs) {
Data.Builder output = new Data.Builder();
Map<String, Object> mergedValues = new HashMap<>();
for (Data input : inputs) {
mergedValues.putAll(input.getKeyValueMap());
}
output.putAll(mergedValues);
return output.build();
}
在WorkWrapper类中调用mConfiguration的getWorkerFactory()方法来获取用来创建Work实例的工厂对象WorkerFactory,在初始化的时候mConfiguration中的mWorkerFactory通过 WorkerFactory.getDefaultWorkerFactory()方法初始化。回到WorkerFactory类的createWorkerWithDefaultFallback()方法中,该默认工厂类的createWorker()方法返回null,因此上面代码worker == null为true。接下来通过反射workerClassName指定的ListenableWorker子类来实例化一个Work对象,所以当该App退出是依旧能获得Work实例,也就能保证Work中的任务得到执行。
2. OneTimeWorkRequest的链式组合
WorkRequest是一个虚类,用于指定Work执行所需的参数,其有两个子类:OneTimeWorkRequest和PeriodicWorkRequest。OneTimeWorkRequest是为单次任务指定参数,OneTimeWorkRequest可以将其所指代的工作进行链式组合。PeriodicWorkRequest是为重复任务指定参数的,不可以组合,在上一节启动任务时对于重复型任务是没有对inpute参数进行合并的。看一下例子:
* <pre>
* A C
* | |
* B D
* | |
* +-------+
* |
* E </pre>
*
* <pre>
* {@code
* WorkContinuation left = workManager.beginWith(A).then(B);
* WorkContinuation right = workManager.beginWith(C).then(D);
* WorkContinuation final = WorkContinuation.combine(Arrays.asList(left, right)).then(E);
* final.enqueue();}</pre>
WorkManager的beginWith()方法和WorkContinuationImpl的then()方法都创建了一个WorkContinuationImpl对象,不同之处是前者的paraent参数为null,看一下then()方法:
//WorkContinuationImpl.java
@Override
public @NonNull WorkContinuation then(@NonNull List<OneTimeWorkRequest> work) {
if (work.isEmpty()) {
return this;
} else {
return new WorkContinuationImpl(mWorkManagerImpl,
mName,
ExistingWorkPolicy.KEEP,
work,
Collections.singletonList(this));
}
}
//WorkContinuationImpl的构造方法
WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
String name,
ExistingWorkPolicy existingWorkPolicy,
@NonNull List<? extends WorkRequest> work,
@Nullable List<WorkContinuationImpl> 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);
}
}
通过then()方法创建一个WorkContinuationImpl对象,并先将父节点列表parents中所有父亲WorkContinuationImpl中的mAllIds保存在自己的mAllIds链表中,再将work链表中所有工作的id保存在mAllIds链表中。WorkContinuation的combine()方法最终是调用的WorkContinuationImpl的combineInternal()方法,最终也是重现创建一个WorkContinuationImpl对象:
@Override
protected @NonNull WorkContinuation combineInternal(
@NonNull List<WorkContinuation> continuations) {
OneTimeWorkRequest combinedWork =
new OneTimeWorkRequest.Builder(CombineContinuationsWorker.class)
.setInputMerger(ArrayCreatingInputMerger.class)
.build();
List<WorkContinuationImpl> parents = new ArrayList<>(continuations.size());
for (WorkContinuation continuation : continuations) {
parents.add((WorkContinuationImpl) continuation);
}
return new WorkContinuationImpl(mWorkManagerImpl,
null,
ExistingWorkPolicy.KEEP,
Collections.singletonList(combinedWork),
parents);
}
这里构造了一个combinedWork做为所有父任务的子任务,该子任务用于合并父任务执行的结果。结合1.1节中立即任务的启动过程可知,没有指定InputMergerFactory的WorkRequest是使用OverwritingInputMerger类来实现合并的,而通过cobine()方法创建的CombineContinuationsWorker的WorkRequest是使用ArrayCreatingInputMerger来实现合并的,看一下其合并实现:
public @NonNull Data merge(@NonNull List<Data> inputs) {
Data.Builder output = new Data.Builder();
Map<String, Object> mergedValues = new HashMap<>();
for (Data input : inputs) {
for (Map.Entry<String, Object> entry : input.getKeyValueMap().entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Class<?> valueClass = value.getClass();
Object mergedValue;
Object existingValue = mergedValues.get(key);
if (existingValue == null) {
// First time encountering this key.
if (valueClass.isArray()) {
// Arrays carry over as-is.
mergedValue = value;
} else {
// Primitives get turned into size 1 arrays.
mergedValue = createArrayFor(value);
}
} else {
// We've encountered this key before.
Class<?> existingValueClass = existingValue.getClass();
if (existingValueClass.equals(valueClass)) {
// The classes match; we can merge.
if (existingValueClass.isArray()) {
mergedValue = concatenateArrays(existingValue, value);
} else {
mergedValue = concatenateNonArrays(existingValue, value);
}
} else if (existingValueClass.isArray()
&& existingValueClass.getComponentType().equals(valueClass)) {
// We have an existing array of the same type.
mergedValue = concatenateArrayAndNonArray(existingValue, value);
} else if (valueClass.isArray()
&& valueClass.getComponentType().equals(existingValueClass)) {
// We have an existing array of the same type.
mergedValue = concatenateArrayAndNonArray(value, existingValue);
} else {
throw new IllegalArgumentException();
}
}
mergedValues.put(key, mergedValue);
}
}
output.putAll(mergedValues);
return output.build();
}
因为涉及许多边界条件,合并的逻辑很复杂,既要考虑到去重,又要考虑到数据类型。
3. 工作调度
首先看一下调度器的创建:
//WorkManagerImpl.java
public @NonNull List<Scheduler> createSchedulers(Context context, TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
// Specify the task executor directly here as this happens before internalInit.
// GreedyScheduler creates ConstraintTrackers and controllers eagerly.
new GreedyScheduler(context, taskExecutor, this));
}
//Schedulers.java
static Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
scheduler = tryCreateGcmBasedScheduler(context);
if (scheduler == null) {
scheduler = new SystemAlarmScheduler(context);
setComponentEnabled(context, SystemAlarmService.class, true);
Logger.get().debug(TAG, "Created SystemAlarmScheduler");
}
}
return scheduler;
}
这里忽略掉GcmBasedScheduler,因为必须要安装GooglePlay才能成功创建该对象。所以这里会创建三种调度器:SystemJobScheduler、SystemAlarmScheduler和GreedyScheduler。对这三种调度器的源码分析,WorkManager之SystemJobScheduler、SystemAlarmScheduler、GreedyScheduler三大调度器,源码流程详细解读这篇文章分析得很详细。
3.1. SystemAlarmScheduler调度过程
其调度过程如图所示:
从上图可以看到最核心的类是SystemAlarmService,该类是链接SystemAlarmScheduler与AlarmManager的桥梁:SystemAlarmScheduler调度任务是通过开启一个SystemAlarmService服务来实现的,调度过程是通过SystemAlarmDispatcher来控制的,调度的实现者是AlarmManager,AlarmManager执行任务时依然是开启一个SystemAlarmService。下面简要看一下具体过程:
先看一下SystemAlarmScheduler调度开始函数:
//SystemAlarmScheduler.java
private void scheduleWorkSpec(@NonNull WorkSpec workSpec) {
Logger.get().debug(TAG, String.format("Scheduling work with workSpecId %s", workSpec.id));
Intent scheduleIntent = CommandHandler.createScheduleWorkIntent(mContext, workSpec.id);
mContext.startService(scheduleIntent);
}
//CommandHandler.java
static Intent createScheduleWorkIntent(@NonNull Context context, @NonNull String workSpecId) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_SCHEDULE_WORK);
intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
return intent;
}
开启了一个SystemAlarmService服务,看一下onStartCommand()方法:
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (mIsShutdown) {
// 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;
}
主要是调用类型为SystemAlarmDispatcher的mDispatcher实例的add()方法:
public boolean add(@NonNull final Intent intent, final int startId) {
assertMainThread();
String action = intent.getAction();
// 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;
}
private void processCommand() {
assertMainThread();
PowerManager.WakeLock processCommandLock =
WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
try {
processCommandLock.acquire();
// Process commands on the background thread.
mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
@Override
public void run() {
synchronized (mIntents) {
mCurrentIntent = mIntents.get(0);
}
if (mCurrentIntent != null) {
final String action = mCurrentIntent.getAction();
final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
DEFAULT_START_ID);
final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", action, startId));
try {
wakeLock.acquire();
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this);
} catch (Throwable throwable) {
} finally {
wakeLock.release();
// Check if we have processed all commands
postOnMainThread(
new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
}
}
}
});
} finally {
processCommandLock.release();
}
}
这里主要是将参数intent添加到类型为List<Intent>的成员mIntents中后然后执行函数processCommand()。在processCommand()函数中先拿到mIntents的第一个元素mCurrentIntent,然后用wakeLock同步加锁执行mCommandHandler对象的onHandleIntent()方法,最后在异步执行一个父类为Runnable的DequeueAndCheckForCompletion对象。看一下onHandleIntent()方法:
//CommandHandler.java
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)) {
} 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 {
Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
}
}
}
}
这里即是根据intent中存储在action中的命令来决定执行对应的方法。这里要确定该action,回忆一下SystemAlarmScheduler的scheduleWorkSpec()方法中是调用CommandHandler的createScheduleWorkIntent()方法来生成一个Intent对象的,其action正好是ACTION_SCHEDULE_WORK。因此这里会调用handleScheduleWorkIntent()方法:
//CommandHandler.java
private void handleScheduleWorkIntent(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
Bundle extras = intent.getExtras();
String workSpecId = extras.getString(KEY_WORKSPEC_ID);
Logger.get().debug(TAG, String.format("Handling schedule work for %s", workSpecId));
WorkManagerImpl workManager = dispatcher.getWorkManager();
WorkDatabase workDatabase = workManager.getWorkDatabase();
workDatabase.beginTransaction();
try {
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
WorkSpec workSpec = workSpecDao.getWorkSpec(workSpecId);
if (workSpec == null) {
return;
} else if (workSpec.state.isFinished()) {
// We need to schedule the Alarms, even when the Worker is RUNNING. This is because
// if the process gets killed, the Alarm is necessary to pick up the execution of
// Work.
return;
}
// Note: The first instance of PeriodicWorker getting scheduled will set an alarm in the
// past. This is because periodStartTime = 0.
long triggerAt = workSpec.calculateNextRunTime();
if (!workSpec.hasConstraints()) {
Alarms.setAlarm(mContext, dispatcher.getWorkManager(), workSpecId, triggerAt);
} else {
Alarms.setAlarm(
mContext,
dispatcher.getWorkManager(),
workSpecId,
triggerAt);
// Schedule an update for constraint proxies
// This in turn sets enables us to track changes in constraints
Intent constraintsUpdate = CommandHandler.createConstraintsChangedIntent(mContext);
dispatcher.postOnMainThread(
new SystemAlarmDispatcher.AddRunnable(
dispatcher,
constraintsUpdate,
startId));
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
}
这里主要是通过调用Alarms类的静态方法setAlarm(),进而调用其静态方法setExactAlarm()获取AlarmManager来执行定时事物:
private static void setExactAlarm(
@NonNull Context context,
@NonNull String workSpecId,
int alarmId,
long triggerAtMillis) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent delayMet = CommandHandler.createDelayMetIntent(context, workSpecId);
PendingIntent pendingIntent = PendingIntent.getService(
context, alarmId, delayMet, PendingIntent.FLAG_UPDATE_CURRENT);
if (alarmManager != null) {
if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.set(RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
}
}
这里通过调用CommandHandler的静态方法createDelayMetIntent()创建一个Intent类型的delayMet对象,进而封装成一个PendingIntent对象传递给AlarmManager,在时间到达设置时间时会执行该delayMet,也是通过其action来决定执行哪一个方法的,看一个该action:
//CommandHandler.java
static Intent createDelayMetIntent(@NonNull Context context, @NonNull String workSpecId) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_DELAY_MET);
intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
return intent;
}
可以看到依然是开启SystemAlarmService服务来处理命令。回到onHandleIntent()方法中,这里action为ACTION_DELAY_MET,因此会执行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);
Logger.get().debug(TAG, String.format("Handing delay met for %s", workSpecId));
// 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.
if (!mPendingDelayMet.containsKey(workSpecId)) {
DelayMetCommandHandler delayMetCommandHandler =
new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
delayMetCommandHandler.handleProcessWork();
} else {
Logger.get().debug(TAG,
String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
workSpecId));
}
}
}
这里主要实例化了一个类型为DelayMetCommandHandler的对象delayMetCommandHandler,并调用其handleProcessWork()来处理工作:
void handleProcessWork() {
mWakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", mWorkSpecId, mStartId));
mWakeLock.acquire();
WorkSpec workSpec = mDispatcher.getWorkManager()
.getWorkDatabase()
.workSpecDao()
.getWorkSpec(mWorkSpecId);
if (workSpec == null) {
stopWork();
return;
}
// Keep track of whether the WorkSpec had constraints. This is useful for updating the
// state of constraint proxies when onExecuted().
mHasConstraints = workSpec.hasConstraints();
if (!mHasConstraints) {
Logger.get().debug(TAG, String.format("No constraints for %s", mWorkSpecId));
onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
} else {
// Allow tracker to report constraint changes
mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
}
}
该函数先通过mWorkSpecId从数据库中拿到对应的workSpec,然后看其是否有约束,如果没有则调用
onAllConstraintsMet()方法执行任务,如果有则让mWorkConstraintsTracker来对该workSpec对象进行跟踪,当约束条件得到满足时会再调用onAllConstraintsMet()方法执行任务。下面分别看一下两个过程。
3.1.1 无约束时启动任务过程
当满足约束条件时,调用onAllConstraintsMet()函数来处理:
//DelayMetCommandHandler.java
@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
// WorkConstraintsTracker will call onAllConstraintsMet with list of workSpecs whose
// constraints are met. Ensure the workSpecId we are interested is part of the list
// before we call Processor#startWork().
if (!workSpecIds.contains(mWorkSpecId)) {
return;
}
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
mCurrentState = STATE_START_REQUESTED;
// Constraints met, schedule execution
// Not using WorkManagerImpl#startWork() here because we need to know if the
// processor actually enqueued the work here.
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
if (isEnqueued) {
// setup timers to enforce quotas on workers that have
// been enqueued
mDispatcher.getWorkTimer()
.startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
} else {
// if we did not actually enqueue the work, it was enqueued before
// cleanUp and pretend this never happened.
cleanUp();
}
} else {
}
}
}
这里先是获取SystemAlarmDispatcher的一个局部对象Processor的startWork()方法来启动任务,然后开启一个十分钟的定时器,当时间超出后如该任务还未结束则会强制结束该任务,若该任务已经添加到执行队列中了则会清除任务。
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
// Work may get triggered multiple times if they have passing constraints
// and new work with those constraints are added.
if (isEnqueued(id)) {
return false;
}
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
return true;
}
这里通过id构建了Runnable的子类WorkerWrapper对象workWrapper,然后获取后台线程来执行该任务。然后该WorkerWrapper的run()方法就会执行,会进一步调用runWorker()方法:
private void runWorker() {
if (tryCheckForInterruptionAndResolve()) {
return;
}
mWorkDatabase.beginTransaction();
try {
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
//...
if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
long now = System.currentTimeMillis();
// Allow first run of a PeriodicWorkRequest
// to go through. This is because when periodStartTime=0;
// calculateNextRunTime() always > now.
// For more information refer to b/124274584
boolean isFirstRun = mWorkSpec.periodStartTime == 0;
if (!isFirstRun && now < mWorkSpec.calculateNextRunTime()) {
resolve(true);
mWorkDatabase.setTransactionSuccessful();
return;
}
}
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
// Merge inputs. This can be potentially expensive code, so this should not be done inside
// a database transaction.
Data input;
if (mWorkSpec.isPeriodic()) {
input = mWorkSpec.input;
} else {
InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
String inputMergerClassName = mWorkSpec.inputMergerClassName;
InputMerger inputMerger =
inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
if (inputMerger == null) {
setFailedAndResolve();
return;
}
List<Data> inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
}
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mWorkDatabase, mForegroundProcessor, mWorkTaskExecutor));
//Builder中的mWorker对象为空,这里会通过反射生成一个ListenableWorker对象
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 {
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) {
} else {
mResult = result;
}
} catch (CancellationException exception) {
} catch (InterruptedException | ExecutionException exception) {
} finally {
onWorkFinished();
}
}
}, mWorkTaskExecutor.getBackgroundExecutor());
} else {
resolveIncorrectStatus();
}
}
该函数比较长,删减部分后结构也很清晰,在执行任务之前会判断该任务是否是定期任务和退避任务,若是则会进一步判断是否满足要求。然后会合并输入,并构造WorkerParameters对象。之后就是开启任务,调用ListenableWorker的startWork()方法启动任务。
3.1.2 约束的更新过程
追踪workSpec的约束是依靠WorkConstraintsTracker实现的,在其构造函数中会初始化一个包含上图七个Controller的数组mConstraintControllers,各个Controller又是通过Tarckers提供的四个BroadcastReceiverConstraintTracker的子类实例来追踪依赖条件的,其监控对应的系统参数的变化,通过接收广播来更新依赖条件是否得到满足。通过replace()函数可以修改追踪对象:
public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
synchronized (mLock) {
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(null);
}
for (ConstraintController<?> controller : mConstraintControllers) {
controller.replace(workSpecs);
}
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(this);
}
}
}
先是将所有的Controller中的callback置为空,然后将所有Controller中的workSpecs替换掉,然后重新设置新的callback。因此当约束满足或者不满足时均会回调WorkConstraintsTracker实现的OnConstraintUpdatedCallback接口方法:
//WorkConstraintsTracker
@Override
public void onConstraintMet(@NonNull List<String> workSpecIds) {
synchronized (mLock) {
List<String> unconstrainedWorkSpecIds = new ArrayList<>();
for (String workSpecId : workSpecIds) {
if (areAllConstraintsMet(workSpecId)) {
Logger.get().debug(TAG, String.format("Constraints met for %s", workSpecId));
unconstrainedWorkSpecIds.add(workSpecId);
}
}
if (mCallback != null) {
mCallback.onAllConstraintsMet(unconstrainedWorkSpecIds);
}
}
}
可以看到当约束条件满足时这里会进一步调用了上一节分析过的areAllConstraintsMet()方法启动任务。下面看一下系统参数变化是如何更新约束条件的,以BatteryChargingTracker为例,其会注册一个广播,广播参数通过getIntentFilter()方法获,当接收到广播后会调用onBroadcastReceive()方法,如下:
//BatteryChargingTracker.java
@Override
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
if (Build.VERSION.SDK_INT >= 23) {
intentFilter.addAction(BatteryManager.ACTION_CHARGING);
intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
} else {
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
}
return intentFilter;
}
@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
Logger.get().debug(TAG, String.format("Received %s", action));
switch (action) {
case BatteryManager.ACTION_CHARGING:
setState(true);
break;
case BatteryManager.ACTION_DISCHARGING:
setState(false);
break;
case Intent.ACTION_POWER_CONNECTED:
setState(true);
break;
case Intent.ACTION_POWER_DISCONNECTED:
setState(false);
break;
}
}
当状态发生变化会调用其父类ConstraintTracker的setState()来更新状态,其逻辑是通过注册是监听器来通知外部:
public void setState(T newState) {
synchronized (mLock) {
if (mCurrentState == newState
|| (mCurrentState != null && mCurrentState.equals(newState))) {
return;
}
mCurrentState = newState;
// onConstraintChanged may lead to calls to addListener or removeListener.
// This can potentially result in a modification to the set while it is being
// iterated over, so we handle this by creating a copy and using that for
// iteration.
final List<ConstraintListener<T>> listenersList = new ArrayList<>(mListeners);
mTaskExecutor.getMainThreadExecutor().execute(new Runnable() {
@Override
public void run() {
for (ConstraintListener<T> listener : listenersList) {
listener.onConstraintChanged(mCurrentState);
}
}
});
}
}