WorkManager高级概念
自定义WorkManager配置和初始化
默认情况下,WorkManager会在应用启动时自动配置,使用适合大多数应用的选项。如果需要更多地控制控制WorkManager的任务管理和调度,需要我们自己来初始化并自定义WorkManager的配置。
WorkManager2.1.0及以后的版本
WorkManager2.1.0有多种方法来配置WorkManager。最灵活的方法是提供一个自定义的初始化WorkManager,需要用到on-demand initialization,可以在2.1.0及以后的版本中用到。
请求式初始化
请求式初始化可以在有必要的情况下才创建WorkManager,而不是每次应用启动时。这样就不需要在应用启动时创建WorkManager,提升应用性能。如何使用:
移除默认的初始化
为了提供自己的配置,需要首先移除默认的初始化。需要更新AndroidManifest.xml,用一个合并的规则:tools:node="remove"。(可在androidx.work的aar中找到AndroidManifest.xml,中有provider和receiver的定义)
AndroidManifest.xml的合并可以从这里看到详细内容。
实现Configuration.Provider
使用Application实现Configuration.Provider接口,并且实现getWorkManagerConfiguration()方法。
当需要使用WorkManager时,确保调用了WorkManager.getInstance(context)方法。WorkManager会调用应用自定义的getWorkManagerConfiguration()方法来实现初始化(不需要手动调用WorkManager.initialize())。
public class WorkApplication extends Application implements Configuration.Provider {
@NonNull
@Override
public Configuration getWorkManagerConfiguration() {
return new Configuration.Builder()
.setMinimumLoggingLevel(Log.INFO)
.build();
}
}
WorkManager2.0.1及以前
针对老版本的WorkManager,有两种初始化选项:
默认初始化
大部分情况下,默认的初始化已经满足要求
WorkManager使用一个自定义的CustomProvider在应用启动时进行初始化。这个是一个内部类:androidx.work.impl.WorkManagerInitializer,并且使用了默认的Configuration。默认的初始化是自动使用的除非显式地禁用。它适合于大多数应用。
自定义初始化
为更精确地控制WorkManager,可以指定自定义的配置。
如果想要控制初始化过程,必须禁用默认的初始化,然后指定自定义的配置。
Configuration cfg = new Configuration.Builder()
.setExecutor()
.setInputMergerFactory()
.setJobSchedulerJobIdRange()
.setMaxSchedulerLimit()
.setMinimumLoggingLevel()
.setTaskExecutor()
.setWorkerFactory()
.build();
WorkManager.initialize(this, cfg);
在WorkManager中进行线程处理
基本的实现能满足大部分应用的需求。对于更高级的用法,例如准确地处理被停止的任务,应该学习一下WorkManager中的线程和并发。
WorkManager提供了4种不同类型的任务:
- Worker是最简单的实现。WorkManager自动用后台线程池去运行
- CoroutineWorker是Kotlin的推荐实现。CoroutineWorker提供了后台任务暂停功能。默认情况下,CoroutineWorker用一个默认的Dispatcher运行,它可以被自定义。
- RxWorker是RxJava2的推荐实现。如果已有了大量的异步代码用RxJava实现,应该用RxWorker。
- ListenableWorker是Worker,CoroutineWorker和RxWorker的基类。它是为那些不使用RxJava2或者必须使用基于回调的异步接口例如
FusedLocationProviderClient的那些用户设计 。
Worker中的线程
当使用Worker,WorkManager自动在一个后台线程中调用Worker.doWork()。这个后台线程来自WorkManager的配置指定的线程池。默认情况下,WorkManager建立了一个线程池,但也可以自定义。例如,可以用应用中已有的线程池,或者创建一个单线程的线程池来保证所有的后台任务串行执行,甚至可以指定有多个线程的线程池。为了自定义线程池,确认已经自定义WorkManager的初始化。
WorkManager.initialize(
context,
new Configuration.Builder()
.setExecutor(Executors.newFixedThreadPool(8))
.build());
串行的执行任务的Worker
public class DownloadWorker extends Worker {
public DownloadWorker(Context context, WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
for (int i = 0; i < 100; ++i) {
if(isStopped()){
break;
}
try {
downloadSynchronously("https://www.google.com");
} catch (IOException e) {
return Result.failure();
}
}
return Result.success();
}
}
其中的Worker.doWork()是同步调用的,应该以阻塞的方法完成全部的后台任务,并且在方法退出时关闭这个任务。如果在doWork()中调用了异步的API并且要返回Result,回调可能会异常。这种情况下,应该尝试使用ListenableWorker。
当正在运行的Worker因为某些原因被终止了,Worker.onStopped()回调会触发。重写这个方法或者调用Worker.isStopped()来检查代码和必要时释放资源。如上述的例子,可以在循环中增加判断。
CoroutineWorker中的线程
对Kotlin使用者,WorkManager提供了对协程的一级支持。需要在构建脚本中引入work-runtime-ktx。应该继承CoroutineWorker而不是继承Worker,它有一些微小的不同。
class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
override varl coroutineContext = Dispatchers.IO
override suspend fun doWork(): Result = coroutineScope {
val jobs = (0 until 100).map {
async {
downloadSynchronously("https://www.google.com")
}
}
// awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure
jobs.awaitAll()
Result.success()
}
}
CoroutineWorker.doWork()是一个suspending function。不像Worker,这些代码不是运行在由Configuration指定的线程池中。相反,它默认使用Dispatchers.Default。可以提供自己的CoroutineContext实现自定义。
RxWorker的线程
框架提供了WorkManager和RxJava2的互用性。需要先引入work-rxjava2的依赖。然后,需要继承RxWorker,而不是Worker。最后重写RxWorker.createWork()方法返回一个Single表示执行的结果。
public class RxDownloadWorker extends RxWorker {
public RxDownloadWorker(Context context, WorkerParameters params) {
super(context, params);
}
@Override
public Single<Result> createWork() {
return Observable.range(0, 100)
.flatMap { download("https://www.google.com") }
.toList()
.map { Result.success() };
}
}
注意,RxWorker.createWork()是在主线程运行,但是返回值是默认在后台线程中监听。可以重写RxWorker.getBackgroundScheduler()来改变监听线程。
流程图
任务的状态变化
- 任务默认是ENQUEUED状态,然后被存入数据库
- 任务如果被调度,能够执行,会进入RUNNING状态
- 如果任务有依赖其他任务,且其他任务未执行完成,则会处于BLOCKED状态;如果依赖的任务执行完成,则会进入ENQUEUED状态,可以被调度执行
- 如果任务依赖其他任务,而其他任务执行失败,则会直接被标记FAILED
- 任务在没有执行完成时,都有可能被取消,置于CANCLED状态
- 成功执行,是SUCCEEDED状态;失败,则为FAILED;运行中也会被取消,则为CANCLED
整体架构图
- Worker负责业务逻辑处理
- WorkRequest配置输入输出,延迟,约束等
- 将任务提交给WorkManager,并存入数据库中
- Schedulers负责任务调度,根据系统版本有不同的调度器
- 任务执行完成后,会更新信息到数据库
类图
调度算法
调度流程图
- 如果API大于等于23,则使用系统的jobscheduler
- 否则如果支持GCMNetWorkManager,则使用GCMScheduler使用google 商店进行任务管理
- 否则用AlarmManager和BroadcastReceiver进行任务调度
Android5.0以下使用Alarm和广播的调度流程
在对应版本的work-runtime的aar包中可以找到AndroidManifest.xml,其中定义了provider和receiver。