一、Request的初始化
val request: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
MainWorker::class.java).build()
其实Request的初始化,主要就是看Builder类的构造器和build()方法
1.Builder的构造器
Builder是OneTimeWorkRequest的内部类,Builder是继承自WorkRequest.Builder。这里先看OneTimeWorkRequest.Builder的构造器
public Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
super(workerClass);
mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();
}
可以看到这里首先调用了super,即调用了WorkRequest.Builder的构造器实现
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
在Builder的构造器中,其实主要就是保存了Worker的子类Class,动态生成一个UUID,并且通过Worker的Class的name添加一个Tag标记,这个Tag是保存在一个Set集合中。
而OneTimeWorkRequest.Builder的build()方法,其实调用的是WorkRequest.Builder的build()方法,但是在build()中调用的buildInternal()的实现是交给WorkRequest.Builder的子类实现的。这里就是在OneTimeWorkRequest.Builder中实现buildInternal()
public final @NonNull W build() {
// 其实就是new一个Request
W returnValue = buildInternal();
// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
// 这里再创建一个新的id和workSpec,是为了WorkRequest.Builder能
// 被使用多次
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mWorkSpec);
mWorkSpec.id = mId.toString();
return returnValue;
}
OneTimeWorkRequest.Builder中buildInternal()实现
@Override
@NonNull OneTimeWorkRequest buildInternal() {
if (mBackoffCriteriaSet
&& Build.VERSION.SDK_INT >= 23
&& mWorkSpec.constraints.requiresDeviceIdle()) {
throw new IllegalArgumentException(
"Cannot set backoff criteria on an idle mode job");
}
if (mWorkSpec.runInForeground
&& Build.VERSION.SDK_INT >= 23
&& mWorkSpec.constraints.requiresDeviceIdle()) {
throw new IllegalArgumentException(
"Cannot run in foreground with an idle mode constraint");
}
return new OneTimeWorkRequest(this);
}
在初始化Request的时候,需要满足几个条件,即设备要在空闲状态下,并且Android版本要大于等于23,也就是Android6.0以上。
其实Request的初始化,就是直接new一个Request,并且持有对应的id、workSpec、tags等信息。workSpec中保存的是Request的各种限制,包括Constraints约束条件。
/**
* 除了上面设置的约束外,WorkManger还提供了以下的约束作为Work执行的条件:
* setRequiredNetworkType:网络连接设置
* setRequiresBatteryNotLow:是否为低电量时运行 默认false
* setRequiresCharging:是否要插入设备(接入电源),默认false
* setRequiresDeviceIdle:设备是否为空闲,默认false
* setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
*/
val constraints: Constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 网络链接中...
/*.setRequiresCharging(true) // 充电中..
.setRequiresDeviceIdle(true) // 空闲时*/
.build()
在WorkRequest.Builder中的setConstraints实现如下:
public final @NonNull B setConstraints(@NonNull Constraints constraints) {
mWorkSpec.constraints = constraints;
return getThis();
}
二、WorkManager执行Request
WorkManager.getInstance(this).enqueue(request)
1.WorkManager.getInstance(@NonNull Context context)
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
WorkManagerImpl.getInstance()中的实现如下:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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;
}
}
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @Nullable WorkManagerImpl getInstance() {
synchronized (sLock) {
if (sDelegatedInstance != null) {
return sDelegatedInstance;
}
return sDefaultInstance;
}
}
虽然WorkManager调用getInstance会初始化WorkManagerImpl对象,但是其实这里并不是第一次初始化调用,实际的第一次初始化调用,是由WorkManagerInitializer这个ContentProvider中的onCreate方法进行的。
在使用Work的时候,编译打包的时候,会在apk的AndroidManifest.xml中会添加一个<provider>内容,如下所示:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.nene.workmanagerdemo.workmanager-init"
android:directBootAware="false" />
android:exported:代表组件能否被其他应用程序组件调用或者交互,false表示不能。
android:multiprocess:如果应该是多进程,这里传true,则每个应用程序进程都会为其提供一个provider对象;如果是false,则应用程序进程共享一个provider对象。
android:directBootAware:为true,表示加密感知。
在apk的AndroidManifest.xml中添加WorkManagerInitializer这个ContentProvider的注册,就是用于WorkManagerImpl的第一次初始化。
WorkManagerInitializer的onCreate方法实现如下:
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
WorkManager.initialize()
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
WorkManagerImpl.initialize()
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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;
}
}
}
可以看到,WorkManagerImpl.initialize()方法就是初始化了WorkManagerImpl的sDelegatedInstance 对象,而WorkManager.getInstance()调用WorkManagerImpl.getInstance()方法,返回的就是sDelegatedInstance对象,所以在程序中调用WorkManager.getInstance()方法的时候,并不会去new一个新的对象,而是直接返回了由ContentProvider进行初始化的对象。
2.WorkManager.enqueue(request)
WorkManager.enqueue实现如下:
public final Operation enqueue(@NonNull WorkRequest workRequest) {
return enqueue(Collections.singletonList(workRequest));
}
其内部调用的是enqueue的重载方法,但是在WorkManager中enqueu的重载方法是一个抽象方法,其实现是在WorkManagerImpl中。
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
// This error is not being propagated as part of the Operation, as we want the
// app to crash during development. Having no workRequests is always a developer error.
if (workRequests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
return new WorkContinuationImpl(this, workRequests).enqueue();
}
在这里,是创建了一个WorkContinuationImpl对象,并且调用其enqueue()方法。
WorkContinuationImpl的构造函数实现如下:
WorkContinuationImpl(
@NonNull WorkManagerImpl workManagerImpl,
@NonNull List<? extends WorkRequest> work) {
this(
workManagerImpl,
null,
ExistingWorkPolicy.KEEP,
work,
null);
}
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);
}
}
在这里,如果是多任务的情况下,会通过调用WorkManagerImpl的beginWith(request)方法创建一个WorkContinuationImpl对象,然后通过调用该WorkContinuationImpl对象的then方法,将当前的WorkContinuationImpl对象作为parent,每次then的时候都会创建一个新的WorkContinuationImpl对象,并且都会将之前的WorkContinuationImpl对象保存的work的id加入到新的WorkContinuationImpl对象中的List<String> mAllIds中。
WorkManager.getInstance(this).beginWith(request).then(request).then(request).enqueue()
在WorkContinuationImpl对象中保存了对应的WorkRequest集合,如果是多任务的时候,则会每个WorkContinuationImpl对象都会有一个其parent保存父WorkContinuationImpl对象,层层向上。而最后一个WorkContinuationImpl对象会保存其所有的父级的workRequest的id值。
接着看WorkContinuationImpl的enqueue()方法
@Override
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
该方法的内部,其实就是通过一个线程池执行一个Runnable任务,然后获取到这个Runnable中得到的Operation对象并返回。
接着看EnqueueRunable的run()方法
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
这里的addToDatabase()方法,其实就是将所有WorkContinuationImpl对象中的WorkRequest集合中的WorkSpec和tag、name等信息保存在数据库中,这里的数据库是采用Room数据库。而在保存数据库的时候,优先保存WorkContinuationImpl中的parents中的request信息,并且是采用递归调用的方式进行保存。
@VisibleForTesting
public boolean addToDatabase() {
WorkManagerImpl workManagerImpl = mWorkContinuation.getWorkManagerImpl();
WorkDatabase workDatabase = workManagerImpl.getWorkDatabase();
workDatabase.beginTransaction();
try {
boolean needsScheduling = processContinuation(mWorkContinuation);
workDatabase.setTransactionSuccessful();
return needsScheduling;
} finally {
workDatabase.endTransaction();
}
}
private static boolean processContinuation(@NonNull WorkContinuationImpl workContinuation) {
boolean needsScheduling = false;
List<WorkContinuationImpl> parents = workContinuation.getParents();
if (parents != null) {
for (WorkContinuationImpl parent : parents) {
// When chaining off a completed continuation we need to pay
// attention to parents that may have been marked as enqueued before.
if (!parent.isEnqueued()) {
// 递归
needsScheduling |= processContinuation(parent);
} else {
Logger.get().warning(TAG, String.format("Already enqueued work ids (%s).",
TextUtils.join(", ", parent.getIds())));
}
}
}
// TODO: 最终都是调用enqueueContinuation进行数据库的insert操作
needsScheduling |= enqueueContinuation(workContinuation);
return needsScheduling;
}
插入成功,则进行WorkRequest的后台调度工作。
即调用EnqueueRunable的scheduleWorkInBackground()方法
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
workManager.getSchedulers()其实就是获取调度器对象的,而这里在初始化调度器的时候,就会创建一个GreedyScheduler。
接着看GreedyScheduler的schedule方法
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mIsMainProcess == null) {
// The default process name is the package name.
mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
}
if (!mIsMainProcess) {
Logger.get().info(TAG, "Ignoring schedule request in non-main process");
return;
}
registerExecutionListenerIfNeeded();
// Keep track of the list of new WorkSpecs whose constraints need to be tracked.
// Add them to the known list of constrained WorkSpecs and call replace() on
// WorkConstraintsTracker. That way we only need to synchronize on the part where we
// are updating mConstrainedWorkSpecs.
List<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);
}
}
}
// onExecuted() which is called on the main thread also modifies the list of mConstrained
// WorkSpecs. Therefore we need to lock here.
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
3.总结
(1)WorkManager的初始化,是通过字节码插桩的方式,修改apk的AndroidManifest.xml文件,然后向AndroidManifest.xml文件中添加对应的ContentProvider和Receiver,用来提供WorkManager的初始化和接收对应的系统服务广播,处理系统级事件
(2)WorkManager的request任务的保存,是保存Room数据库中
(3)WorkManager中执行request任务的时候,是通过线程池执行Runnable任务,而request都是封装在对应的Runnable中