默认情况下,当app启动的时候WorkManager自动的完成了配置,这个配置适用于大多数app.如果我们需要对WorkManager有更多的控制,比如:管理和调度任务,可以使用自己的配置初始化WorkManager.
这里有三种初始化的方式:
Default initialization
在应用启动的时候,WorkManager通过一个自定义的ContentProvider来初始化自己。相关代码在androidx.work.impl.WorkManagerInitializer中,同时使用默认的配置。除非你明确的禁止,否则默认的初始化将会自动完成。
如果想要使用自己的配置,首先需要移除默认的初始化器。步骤如下:
移除默认初始化器
添加自定义配置
// provide custom configuration
Configuration myConfig = new Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
//initialize WorkManager
WorkManager.initialize(this, myConfig);
Note: On-demand initialization is available in WorkManager 2.1.0-alpha01 and higher.
自定义初始化WorkManager是最灵活、合适的方式。这种方式允许你只有在需要的时候才初始化,而不是每次app启动的时候。这样,WorkManager的初始化就不在应用的启动配置列表中,优化了应用启动的性能。
实现步骤如下:
class MyApplication extends Application implements Configuration.Provider {
@Override
public Configuration getWorkManagerConfiguration() {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}
前面我们提到了WorkManager可以代替我们完成后台异步操作。基本实现可以满足大多数应用的需求。对于更高级别的使用案例,例如正确的处理被停止的工作任务,我们需要聊了解下WorkManager中的线程和并发。
WorkManager提供了四种工作场景:
当我们使用一个Worker的时候,WorkManager自动地在一个后台线程调用doWork()方法,这个后台线程来源于WorkManager的配置中指定的一个线程池。默认情况,系统提供了一个默认的线程池给我们,但是我们可以自己指定使用的线程池。例如,共用已经存在的线程池、或者创建一个单线程线程池,以确保所有后台任务都顺序执行,或者仅仅是指定一个不同容量的线程池。指定自己的线程池,首先要确保已经允许了手动初始化WorkManager。配置WorkManager的时候,我们可以像下面这样指定线程池。
WorkManager.initialize(
context,
new Configuration.Builder()
.setExecutor(Executors.newFixedThreadPool(8))
.build());
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();
}
}
针对Kotlin用户,WorkManager提供了一等类支持coroutines。首选需要在gradle中配置相关依赖,上面已经详见上文。然后继承CoroutineWorker。下面是一个例子:
class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
override val 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(可挂起)方法。其中的方法,并不运行在配置中指定的线程池中。默认它运行在Dispatchers.Default中,通过提供CoroutineContext我们可以指定自己的Dispatchers。如上面代码所示。
CoroutineWorker通过取消coroutine和生成取消信号自动的处理停止任务,我们无需做任何额外的额操作。
系统提供了WorkManager和RxJava2之间相互操作的方法。首先需要在gradle中配置相关依赖,详见上文。然后继承RxWorker,最后重写createWork()方法,这个方法返回一个Single代表着执行的结果。下面是个例子:
public class RxDownloadWorker extends RxWorker {
public RxDownloadWorker(Context context, WorkerParameters params) {
super(context, params);
}
@Override
public Single createWork() {
return Observable.range(0, 100)
.flatMap { download("https://www.google.com") }
.toList()
.map { Result.success() };
}
}
注意RxWorker.createWork()在主线程中调用,但是返回的值默认在后台线程中订阅。我们可以重写RxWorker.getBackgroundScheduler()改变订阅线程。
停止一个RxWorker系统会自动妥善的处理Observers,我们无需做任何额外操作。
一定会有一个场景,我们需要提供一个线程策略。比如:我们也许需要处理一个异步的回调操作,很明显我们不能仅仅指望一个Worker,因为它不支持代码块阻塞。WorkManager通过提供ListenableWorker支持这种案例。ListenableWorker是最低级别的worker API;Worker、CoroutineWorker和RxWorker 都是源自这个类。ListenableWorker仅提供任务需要开始和结束的标识,线程完全由我们自己来控制。开始任务的标识是在主线程执行的,所以我们手动选择执行在后台线程很重要。
抽象方法ListenableWorker.startWork() 返回一个Result的ListenableFuture。ListenableFuture是一个轻量级的接口;它是一个提供附着监听并且处理异常的Future。当后台操作完成时该方法返回一个带有结果的ListenableFuture。我们可以通过以下2个方法中的一个创建ListenableFutures。
public class CallbackWorker extends ListenableWorker {
public CallbackWorker(Context context, WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public ListenableFuture startWork() {
return CallbackToFutureAdapter.getFuture(completer -> {
Callback callback = new Callback() {
int successes = 0;
@Override
public void onFailure(Call call, IOException e) {
completer.setException(e);
}
@Override
public void onResponse(Call call, Response response) {
++successes;
if (successes == 100) {
completer.set(Result.success());
}
}
};
completer.addCancellationListener(cancelDownloadsRunnable, executor);// 添加一个取消监听
for (int i = 0; i < 100; ++i) {
downloadAsynchronously("https://www.google.com", callback);
}
return callback;
});
}
}