本篇分析的是WorkManger版本2.3.1的流程
因为jetpack目前更新频率较快,可能过几周这部分代码会有变动
private fun test(){
WorkManager.getInstance(this)
.enqueue(OneTimeWorkRequest.Builder(Worker::class.java)
.build())
}
方法调用只有两步,获取单例以及插入任务队列
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
WorkManagerImpl instance = getInstance();
if (instance == null) {
Context appContext = context.getApplicationContext();
if (appContext instanceof Configuration.Provider) {
initialize(
appContext,
((Configuration.Provider) appContext).getWorkManagerConfiguration());
instance = getInstance(appContext);
} else {
throw new IllegalStateException("WorkManager is not initialized properly. You "
+ "have explicitly disabled WorkManagerInitializer in your manifest, "
+ "have not manually called WorkManager#initialize at this point, and "
+ "your Application does not implement Configuration.Provider.");
}
}
return instance;
}
}
看似是单例初始化入口,但这的applicationConext并不匹配Configuration.Provider,所以这里是不会走的
真正初始化方法是在这里
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
...
}
这个ContentProvider会随着应用启动而初始化,然后去调用WorkManger的第二个初始化方法
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("WorkManager is already initialized. Did you "
+ "try to initialize it manually without disabling "
+ "WorkManagerInitializer? See "
+ "WorkManager#initialize(Context, Configuration) or the class level "
+ "Javadoc for more information.");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
-----------------------------------------------------------------------------------------
public class WorkManagerTaskExecutor implements TaskExecutor {
private final SerialExecutor mBackgroundExecutor;
public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
// Wrap it with a serial executor so we have ordering guarantees on commands
// being executed.
mBackgroundExecutor = new SerialExecutor(backgroundExecutor);
}
...
}
这个方法中会创建一个TaskExecutor,内部指定的Executor是SerialExecutor,也就是说后续的任务都是以串行的形式提交执行
创建构造方法的同时会创建一个Schedulers的集合,同时会创建一个Processor对象,这个对象中会有
startWork方法,后面会用到;同时这里会有一个WorkDatabase用作数据存储,而这个DataBase也用到了jetpack的RoomDatabase数据库
public abstract class WorkDatabase extends RoomDatabase { ... }
------------------------------------------------------------------
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase database) {
Context applicationContext = context.getApplicationContext();
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
而这个schedulers里会放两个Scehduler对象,一个是GreedyScheduler,另一个会根据版本选择SystemJobScheduler或者是SystemAlarmScheduler
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
new GreedyScheduler(context, taskExecutor, this));
}
static Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {//api 23
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;
}
GreedyScheduler的schedule方法会去检查当前的约束,如果没有则马上去执行任务,但应用必须活跃才能执行;而另外两个运行时间比第一个稍慢,但存活周期比第一个要长
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
enqueue方法会有是否已经插入的判断,然后用上面创建的SerialExecutor去执行任务, 并把当前的操作对象返回
public class EnqueueRunnable implements Runnable {
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
}
addToDatabase方法主要会进行一些约束和id校验,然后录入数据信息
比如之前插入的任务处于FAILED,CANCELED 等状态时,返回false ;
或者设置enqueueUniqueWork唯一id标示任务执行时,当Policy设置成KEEP,而任务队列里已经有同id的任务处于ENQUEUED或者RUNNING状态时也返回false 等;
当满足条件时,就会插入任务队列
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecs;
workDatabase.beginTransaction();
try {
eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
long now = System.currentTimeMillis();
// Mark all the WorkSpecs as scheduled.
// Calls to Scheduler#schedule() could potentially result in more schedules
// on a separate thread. Therefore, this needs to be done first.
for (WorkSpec workSpec : eligibleWorkSpecs) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now);
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
这个方法前面都是些数据库操作,到最后会遍历满足的条件的WorkSpec,用上面创建的两个Scheduler的集合遍历去处理这个;
注意这里有个eligibleWorkSpecs的集合,这个会最终转成WorkSpec集合交付scheduler去处理,而这个处理方式也就是串并行的区别,串行每次都只会获取到一个,而并行会获取多个
同时这里也指定了同时能获取到的最大个数getMaxSchedulerLimit,也是数据库查询的限制,默认是20,但在SDK版本为23的时候限制是10个
比如GreedyScheduler的schedule方法
public void schedule(@NonNull WorkSpec... workSpecs) {
......
List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
List<String> constrainedWorkSpecIds = new ArrayList<>();
for (WorkSpec workSpec : workSpecs) {
if (workSpec.state == WorkInfo.State.ENQUEUED
&& !workSpec.isPeriodic()
&& workSpec.initialDelay == 0L
&& !workSpec.isBackedOff()) {
if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
// Ignore requests that have an idle mode constraint.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
// Ignore requests that have content uri triggers.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
......
}
这里会有一个循环遍历,根据上面传递过来的集合数可以确定是单任务串行,还是多任务并行处理;
当任务状态是ENQUEUED,而且也没有做定时,延迟执行或者有重试的,都会进这个方法
然后先判断约束条件,如果没有约束条件都会马上执行startWork方法,相反如果有约束条件则是另一种情况,会走广播
又比如SystemJobService的onStartJob方法,最终也会走到startWork方法中,SystemJobScheduler的schedule种也有对多任务的遍历逻辑,这里就不列出了
public boolean onStartJob(@NonNull JobParameters params) {
if (mWorkManagerImpl == null) {
Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
jobFinished(params, true);
return false;
}
String workSpecId = getWorkSpecIdFromJobParameters(params);
if (TextUtils.isEmpty(workSpecId)) {
Logger.get().error(TAG, "WorkSpec id not found!");
return false;
}
synchronized (mJobParameters) {
if (mJobParameters.containsKey(workSpecId)) {
Logger.get().debug(TAG, String.format(
"Job is already being executed by SystemJobService: %s", workSpecId));
return false;
}
Logger.get().debug(TAG, String.format("onStartJob for %s", workSpecId));
mJobParameters.put(workSpecId, params);
}
WorkerParameters.RuntimeExtras runtimeExtras = null;
if (Build.VERSION.SDK_INT >= 24) {
runtimeExtras = new WorkerParameters.RuntimeExtras();
if (params.getTriggeredContentUris() != null) {
runtimeExtras.triggeredContentUris =
Arrays.asList(params.getTriggeredContentUris());
}
if (params.getTriggeredContentAuthorities() != null) {
runtimeExtras.triggeredContentAuthorities =
Arrays.asList(params.getTriggeredContentAuthorities());
}
if (Build.VERSION.SDK_INT >= 28) {
runtimeExtras.network = params.getNetwork();
}
}
mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
return true;
}
startWork方法会用之前创建的TaskExecutor,也就是单次顺序执行的Executor
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
然后这个会走到Processor的startWork方法中
public class Processor implements ExecutionListener, ForegroundProcessor {
......
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
......
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;
}
}
然后在WorkWrapper的run方法中, 最终会执行到Worker的startWork方法中
public class WorkerWrapper implements Runnable {
......
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
......
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
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);
}
}
});
final String workDescription = mWorkDescription;
future.addListener(new Runnable() {
@Override
public void run() {
try {
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();
}
}
// Package-private for synthetic accessor.
void onWorkFinished() {
boolean isWorkFinished = false;
if (!tryCheckForInterruptionAndResolve()) {
mWorkDatabase.beginTransaction();
try {
WorkInfo.State state = mWorkSpecDao.getState(mWorkSpecId);
mWorkDatabase.workProgressDao().delete(mWorkSpecId);
if (state == null) {
resolve(false);
isWorkFinished = true;
} else if (state == RUNNING) {
handleResult(mResult);
state = mWorkSpecDao.getState(mWorkSpecId);
isWorkFinished = state.isFinished();
} else if (!state.isFinished()) {
rescheduleAndResolve();
}
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
}
if (mSchedulers != null) {
if (isWorkFinished) {
for (Scheduler scheduler : mSchedulers) {
scheduler.cancel(mWorkSpecId);
}
}
Schedulers.schedule(mConfiguration, mWorkDatabase, mSchedulers);
}
}
------------------------------------------------------------------------------
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) {
Logger.get().error(TAG, "Could not instantiate " + workerClassName, e);
}
}
}
if (worker != null && worker.isUsed()) {
String factoryName = this.getClass().getName();
String message = String.format("WorkerFactory (%s) returned an instance of a "
+ "ListenableWorker (%s) which has already been invoked. "
+ "createWorker() must always return a new instance of a "
+ "ListenableWorker.",
factoryName, workerClassName);
throw new IllegalStateException(message);
}
return worker;
}
这里会先调用createWorkerWithDefaultFallback方法反射创建一个Worker,而这里的workerClassName就是我们自定义Work
当操作完成后,会走onWorkFinished方法,这里会检查任务的状态;这里的isWorkFinished字段表示中,SUCCEEDED,FAILED,CANCELLED都会返回true,标记的是一个任务的执行情况,只要执行了一遍,不论是否正常执行完都是Finish标记;
最后会回到Schedulers.schedule方法中,继续判断下一步的逻辑,是否有下一个任务需要执行等,也就形成了一个循环,而正常的串行多任务执行就是走的这个循环,具体条件判断WorkManager内部已经处理完毕了,这里就不细扣了
这里区分一下串行和并行,Work的创建也是有些区别的
串行是按我们配置的顺序依次执行进这个方法的,也就是会按我们自定义的Worker;而并行是开始创建了一个CombineContinuationsWorker,同时这个Worker会放到一个OneTimeWorkRequest中,然后这个的doWork方法中什么都不做,直接返回Success,也就是说并行首次反射创建的是CombineContinuationsWorker这个,马上返回成功,然后走到Schedulers.schedule方法进入下一轮循环时候,才会去取多任务并行,而这之后才会去执行我们的Worker
而Worker的startWork方法最终也就调用了我们重写的doWork方法
@Override
public final @NonNull ListenableFuture<Result> 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;
}
具体className赋值是在Builder中
public abstract static class Builder<B extends Builder<?, ?>, W extends WorkRequest> {
boolean mBackoffCriteriaSet = false;
UUID mId;
WorkSpec mWorkSpec;
Set<String> mTags = new HashSet<>();
Class<? extends ListenableWorker> mWorkerClass;
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
......
}
大致流程图是这样
这里应该少了一个每个任务执行完循环schedule的路线
1.WorkManager初始化在WorkManagerInitializer中,而这个是继承ContentProvider,也就是应用一启动就会初始化
2.创建WorkRequest时会传入我们创建的Work的类名,而这个类名同时也会添加到WorkRequest的Tag集合中
3.每次创建新的WorkRequest都会调用 UUID.randomUUID()创建一个唯一id,并把这个id和上面传的类名一起保存在WorkSpec成员变量中
4.初始化时会创建一个Schedulers集合,里面放的一个是GreedyScheduler,而另一个会根据版本区分,当版本>=23时,用的是SystemJobScheduler,否则用的是SystemAlarmService
5.每次插入新的任务都会从数据查询进行校验,比如约束条件或者之前任务状态,唯一性等的校验,然后把任务以及相应的状态插入或更新到数据库中
6.初始化会创建一个WorkManagerTaskExecutor,而这个默认的后台执行者是SerialExecutor,也就是默认会是按照顺序依次执行任务
7.开始执行任务前会去校验约束,延迟,以及任务状态Status等信息,不满足会等待满足条件再执行
8.执行任务时会遍历上面的两个Scheduler都去执行,一个是立即执行,如果有延迟等信息,则会用第二个延迟执行
9.任务的最终执行时从数据库取得任务信息,然后继续对任务进行一系列校验,如果满足,那么就会反射创建我们自定义的Worker对象,并执行doWork方法
10.执行完当前任务后,会调用Schedulers.schedule方法继续下一个任务的逻辑判断,非指定则任务会是一个个按顺序执行,这里的顺序是指阻塞的,指的是任务执行的顺序是依次的,也就是说要等第一个任务执行完才执行第二个
11.并行任务首次执行会走CombineContinuationsWorker,而这个Worker会马上返回成功,之后循环回来执行我们自定义的Worker