一、WorkManager简介
我们先来看下官网是对WorkManager如何介绍的。
The WorkManager API makes it easy to schedule deferrable,
asynchronous tasks that are expected to run even if the app exits or device restarts.
简单翻译:WorkManager API可以让你更加容易的管理一些后台任务,即使你的应用是退出或者设备重启的状态。
其实就是"管理一些要在后台工作的任务, -- 即使你的应用没启动也能保证任务能被执行".
WorkManager is intended for tasks that are deferrable—that is,
not required to run immediately—and required to run reliably even if the app exits or the device restarts. For example:
Sending logs or analytics to backend services
Periodically syncing application data with a server
WorkManager适用于任务的延迟执行,不需要立即执行,并且可以可靠的执行即使应用退出了或者设备重启。例如:
向后端服务发送日志分析
定期与服务器同步应用程序数据
WorkManager is not intended for in-process background work that can safely
be terminated if the app process goes away or for tasks that require immediate execution.
WorkManager 不适用那些在应用进程消失的时候,可以安全被终止的后台任务。
翻译比较拗口,也就是说,WorkManager 并不会因为应用进程的结束而被终止。
针对官网对WorkManager介绍的小结:
在应用的开发中,WorkManager可以作为管理任务的组建,并且和传统Service后台任务还是有区别的,WorkManager不会因为应用进程的消失而被终止,即使设备被重启。而Service 是做不到的。Service 更加适用于应用内的后台任务。并且WorkManager可以设置延迟执行,不需要马上执行,也就是我们常说的定时任务。
举个例子:当我们要开发一款安全检查软件的时候,需要在施工现场拍照,拍视频,并把现场的情况录入到系统中,但是往往现场的网络是差的,这个时候我们可以先把数据保存到本地数据库,然后通过WorkManager设置定时任务,在有网的情况上传数据。
二、使用WorkManager调度任务
当WorkManager在应用程序中执行某一个任务,WorkManager则会开一个线程来执行当前任务,假如应用没有启动,WorkManager会选择合适的方式来安排这个后台任务。我们不需要关心选择的是什么方式,我们可以将任务交给WorkManager,让WorkManager选择一个合适的方式来安排任务的执行。
此外WorkManager还提供了某些高级的功能,比如我们可以建立一连串的任务让WorkManager执行,WorkManager会按照顺序执行这些任务。我们还可以观察任务的LiveData,检查任务的返回值,如果需要在UI做相应的展示,这个特性是非常有用的。
三、WorkManager的集成
我们需要添加WorkManager的依赖。
def work_version = "1.0.0-alpha01"
implementation "android.arch.work:work-runtime:$work_version" // use -ktx for Kotlin
// optional - Firebase JobDispatcher support
implementation "android.arch.work:work-firebase:$work_version"
// optional - Test helpers
androidTestImplementation "android.arch.work:work-testing:$work_version"
四、主要相关类的介绍
Worker:
Worker是具体要执行任务的类,我们的任务就是在Worker中执行
WorkRequest:
把Worker包装成WorkRequest,并加入队列,WorkRequest中多了一些属性,比如:
1、ID 任务的唯一性
2、何时执行任务
3、执行有没有环境的限制(如只有有网的时候才执行)
4、任务执行链条(某个任务执行完,才到我执行)
WorkManager
WorkManager负责把WorkRequest加入到队列,并管理WorkRequest
WorkStatus
WorkStatus包含了某个任务的相关信息,WorkManager为每个WorkRequest提供了一个LiveData对象,而
LiveData对象中包含了WorkStatus信息,通过观察LiveData对象的变化获取WorkStatus的信息,
以便确认当前任务的状态。
五、典型例子
下面我们通过一个例子来介绍以上各个类的使用方式。
假如我们要开发一个图库软件,需要定时对图库里的图片进行压缩。那么我们知道,我们的任务是压缩。
1、首先我们创建Worker的实现类,我们在doWork方法中执行压缩的任务。
package com.example.administrator.workmanager;
import android.support.annotation.NonNull;
import android.util.Log;
import androidx.work.Worker;
public class MyWorker extends Worker {
private static final String TAG = "MyWorker";
@NonNull
@Override
public WorkerResult doWork() {
//压缩方法
compress();
return WorkerResult.SUCCESS;
}
/**
* 压缩图片任务
*/
private void compress() {
Log.e(TAG, "compress: 对图片进行压缩" );
}
}
2、把我们创建的Worker包装成WorkRequest,在这我们使用系统的OneTimeWorkRequest
//把 MyWorker 包装成 OneTimeWorkRequest
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();
我们看下OneTimeWorkRequest源码
/**
* A class that represents a request for non-repeating work.
*/
public final class OneTimeWorkRequest extends WorkRequest {
大概意思是指OneTimeWorkRequest包装的任务不需要重复执行,只执行一次。
3、我们通过WorkManager把OneTimeWorkRequest加入到队列
WorkManager.getInstance().enqueue(request);
在条件允许的情况下,如果我们没有指定任何约束,WorkManager会立即执行我们的任务。
我们可以看到控制台,我们压缩方法执行了
MyWorker: compress: 对图片进行压缩
检查任务状态
以上我们通过WorkManager完成了对任务的执行,我们来看下如何检查任务的状态
WorkManager.getInstance().getStatusById(request.getId()).observe(this, new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
if(workStatus!=null&&workStatus.getState().isFinished()){
Log.e(TAG, "onChanged: 任务完成了" );
}
}
});
我们通过request的id获取到任务的状态,当任务完成的时候,
workStatus.getState().isFinished()
我们看控制台打印:
MainActivity: onChanged: 任务完成了
添加约束
假如我们想要给任务添加约束条件,比如在有网的情况下才执行任务。
我们收需要创建Constraints对象 并设置网络条件为连接状态
Constraints myConstraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
// Many other constraints are available, see the
// Constraints.Builder reference
.build();
在创建WorkRequest对象的时候 传入Constraints对象
//把 MyWorker 包装成 OneTimeWorkRequest
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(myConstraints).build();
我们做一个测试分别在有网和没网的之后启动任务。
我们启动应用程序之后,关闭网络,任务不会执行,再打开网络的时候,任务执行了。
取消任务
加入到任务队列的任务还可以进行取消,通过任务ID取消某个具体的任务
WorkManager.getInstance().cancelWorkById(request.getId());
六、高级功能
WorkManager Api还提供了一些高级功能
1、重复执行任务
以上的例子我们能对图片不止一次压缩,有可能多次压缩,定时压缩。这个时候我们用到的是PeriodicWorkRequest这个类,和上面一样 我们把任务封装成PeriodicWorkRequest,然后我们设置每隔12个小时压缩一次。然后我们在通过WorkManager把PeriodicWorkRequest加入到任务队列即可。
//把 MyWorker 包装成 PeriodicWorkRequest
PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(MyWorker.class,12,TimeUnit.HOURS).
setConstraints(myConstraints).build();
WorkManager.getInstance().enqueue(request);
2、链式任务
有时候我们需要按照顺序执行一串任务,先执行某一个再到某一个,我们可以进行如下操作。创建三个任务A B C,按照顺序执行。
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class)
.setConstraints(myConstraints).build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(BWorker.class)
.setConstraints(myConstraints).build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(CWorker.class)
.setConstraints(myConstraints).build();
WorkManager.getInstance().beginWith(requestA).then(requestB).then(requestC).enqueue();
我们也可以一次性执行多个。
WorkManager.getInstance().beginWith(requestA,requestB).then(requestB,requestC).then(requestC).enqueue();
3、复杂的链式任务
我们可以通过 WorkContinuation.combine()建立复杂的任务链
比如我们想完成如下图的任务链:
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class).setConstraints(myConstraints).build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(BWorker.class).setConstraints(myConstraints).build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(CWorker.class).setConstraints(myConstraints).build();
OneTimeWorkRequest requestD = new OneTimeWorkRequest.Builder(DWorker.class).setConstraints(myConstraints).build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(EWorker.class).setConstraints(myConstraints).build();
WorkContinuation c1 = WorkManager.getInstance().beginWith(requestA).then(requestC);
WorkContinuation c2 = WorkManager.getInstance().beginWith(requestB).then(requestD);
WorkContinuation c =WorkContinuation.combine(c1,c2).then(requestE);
c.enqueue();
4、唯一的工作序列
我们可以通过beginUniqueWork方法执行唯一序列的工作任务。该方法有三个参数:uniqueWorkName唯一工作序列名称,work需要执行的任务,existingWorkPolicy代表当有重复的任务需要做什么操作。这个参数有三个枚举:
1、REPLACE: 用新任务来取代已经存在的任务
2、KEEP: 保留已经存在的任务. 忽视新任务
3、APPEND: 新任务入列. 新旧任务都存在于队列中.
public final WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull OneTimeWorkRequest... work)
具体使用方式:
WorkManager.getInstance().beginUniqueWork("a",ExistingWorkPolicy.KEEP,requestA);
5、设置标签
我们之前是根据任务的ID获取任务,我们也可以给任务设置标签Tag,通过Tag来获取任务。
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(AWorker.class)
.addTag("tag").setConstraints(myConstraints).build();
WorkManager.getInstance().getStatusesByTag("tag");
6、输入参数和返回值
我们可以给任务传递参数,也可以获取任务的返回结果。
给任务传递参数
1、传递参数
我们首先需要创建需要传递的数据,key value的形式。然后再创建WorkRequest对象的时候进行设置。
Data data = new Data.Builder().putInt("a",1).putInt("b",2).build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(EWorker.class).setInputData(data).build();
WorkManager.getInstance().enqueue(requestE);
2、在任务中获取传递来的参数
package com.example.administrator.workmanager;
import android.support.annotation.NonNull;
import android.util.Log;
import androidx.work.Worker;
public class EWorker extends Worker {
private static final String TAG = "MyWorker";
@NonNull
@Override
public WorkerResult doWork() {
//压缩方法
compress();
int a = getInputData().getInt("a",0);
int b = getInputData().getInt("b",0);
Log.e(TAG, "doWork: "+a+":"+b );
return WorkerResult.SUCCESS;
}
/**
* 压缩图片任务
*/
private void compress() {
Log.e(TAG, "compress: 对图片进行压缩" );
}
}
我们看打印结果,确实是我们传递过来的参数。
03-20 17:37:00.053 25227-25258/com.example.administrator.workmanager E/MyWorker: compress: 对图片进行压缩
03-20 17:37:00.053 25227-25258/com.example.administrator.workmanager E/MyWorker: doWork: 1:2
获取任务的返回结果
1、获取任务中的返回值,即获取任务结果
我们在任务中创建数据,并调用setOutputData方法,传递出来
package com.example.administrator.workmanager;
import android.support.annotation.NonNull;
import android.util.Log;
import androidx.work.Data;
import androidx.work.Worker;
public class EWorker extends Worker {
private static final String TAG = "MyWorker";
@NonNull
@Override
public WorkerResult doWork() {
//压缩方法
compress();
Data data = new Data.Builder().putInt("a",1).putInt("b",2).build();
setOutputData(data);
return WorkerResult.SUCCESS;
}
/**
* 压缩图片任务
*/
private void compress() {
Log.e(TAG, "compress: 对图片进行压缩" );
}
}
2、获取任务的返回结果
WorkManager.getInstance().getStatusById(requestE.getId()).observe(this, new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
if(workStatus!=null){
int result = workStatus.getOutputData().getInt("a",0);
Log.e(TAG, "onChanged: "+result );
}
}
})
我们看打印结果:
E/MainActivity: onChanged: 1
这样我们就得到了任务中的数据1
七、小结
通过以上学习我们知道
1、WorkManager可以作为执行后台任务的组建,并且不依赖应用的是否退出
2、可以执行多个任务,并设计任务链
3、可以添加约束在指定的环境,时间执行任务。
4、可以向任务传递参数,也可以获取任务的返回值