WorkManager介绍
它的主要作用则是管理在后台工作的任务,即使APP没有启动,它也能保证任务可以被执行。这样的好处可以提高应用的效率 , 降低耗电量 , 同时通过约束的方式来限制功能的上传。
WorkManager机制
在5.0以上的版本WorkManager会通过JobScheduler或者Firebase的JobDispatcher来实现,而在5.0以下的版本,则会通过AlarmManager来实现
它有以下好处:
- 支持异步的单次、定时的任务
- 支持网络条件、存储空间、充电状态等条件的约束
- 支持复杂的并行的链式任务
- 某个Work Request的输出作为下一个Work Request的输入
- 仅支持Target 14以上的API
- 遵循系统的健康
- 支持LiveData将请求的状态同步到UI
但是WorkManager仍然无法代替线程池、AsyncTask,例如以下的例子都可以使用WorkManager:上传日志,实现图片的滤镜并且保存图片,定期从网络同步本地数据。
使用WorkManager
- 导入WorkManager
在app的Module中导入Kotlin的WorkManager。而目前的版本号已经到了1.0.0-alpha13
。
dependencies {
implementation "android.arch.work:work-runtime-ktx:$versions.work"
}
- 创建Worker
创建一个类,继承自Worker,并且实现doWork
函数,返回任务执行的结果,并且在任务中可以携带数据返回结果
, 而任务结果可以在LiveData
中获取。
class TestWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
private val TAG by lazy { TestWorker::class.java.simpleName }
private val KEY_EXCEPTION by lazy { "KEY_EXCEPTION" }
private val KEY_SUCCESS by lazy { "KEY_SUCCESS" }
override fun doWork(): Result {
return try {
Log.e(TAG, "Worker Do Work")
val successData = Data.Builder().putString(KEY_SUCCESS, "Success").build()
Result.success(successData)
} catch (exception: Exception) {
val exceptionData = Data.Builder().putString(KEY_EXCEPTION, "Exception :${exception.message}").build()
Result.failure(exceptionData)
}
}
}
- 将Worker加入队列中
在创建完任务后,需要将该任务加入WorkManager的队列中。在以下代码中,创建了只执行一次的任务OneTimeWorkRequest
,并且携带了输入的参数setInputData
,创建完后,就可以将它插入到WorkManager的队列中等待执行。
val inputData = Data.Builder().putString("KEY_INPUT", "input data").build()
val workRequest = OneTimeWorkRequest.Builder(TestWorker::class.java).setInputData(inputData).build()
WorkManager.getInstance().enqueue(workRequest)
通过
PeriodicWorkRequest
创建的任务,是会定期执行的,需要传入定期的时间即可
创建链式任务
当某些任务具有依赖关系时候(如A依赖B完成的结果,B又依赖C完成的结果),则需要使用链式任务。
可以通过WorkManager.beginWith
来开始执行任务,并且通过then
来将后续的任务链接上。并且可以将前一个任务的输出作为后一个任务的输入。
val workA = OneTimeWorkRequest.Builder(TestWorker::class.java).build()
val workB = OneTimeWorkRequest.Builder(BlurWorker::class.java).build()
val workC = OneTimeWorkRequest.Builder(CleanupWorker::class.java).build()
WorkManager.getInstance().beginWith(workA).then(workB).then(workC).enqueue()
在doWork
中通过getInputData
来获取上一个任务传递的参数
class BlurWork extends Worker {
public BlurWork@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public ListenableWorker.Result doWork() {
getInputData().getKeyValueMap().get("Key");
return Result.success();
}
}
如果只允许单独的任务存在的话,则需要通过beginUniqueWork
来开启任务。当存在重复的任务时,会采用传入的ExistingWorkPolicy
来对已存在的任务进行策略处理。
WorkManager.getInstance().beginUniqueWork("workName",ExistingWorkPolicy.REPLACE,workA).enqueue()
多链式任务
例如有任务需要等待多个链式任务完成的话 , 则可以通过WorkManager来实现.
通过以下方式来完成链式任务的执行.
WorkContinuation chain1 = WorkManager.getInstance(myContext)
.beginWith(workA)
.then(workB);
WorkContinuation chain2 = WorkManager.getInstance(myContext)
.beginWith(workC)
.then(workD);
WorkContinuation chain3 = WorkContinuation
.combine(Arrays.asList(chain1, chain2))
.then(workE);
chain3.enqueue();
使用LiveData监听任务状态
在任务执行的过程中,可以通过LiveData
来监听任务的状态,所有的任务都具有这几种状态:
- BLOCKED:阻塞
- CANCELLED:被取消
- ENQUEUED:入队列
- FAILED:失败
- RUNNING:正在运行
- SUCCEEDED:运行成功
- 通过
addTag
设置任务的Tag
val workRequest = OneTimeWorkRequest.Builder(TestWorker::class.java).addTag("workRequest").build()
WorkManager.getInstance().enqueue(workRequest)
- 在
1.0.0-alpha10
版本中,还可以通过WorkManager.getInstance().getStatusesByTagLiveData()
获取对应的Worker的Status。
而1.0.0-alpha13
版本中,已经没有该函数了,已经替换成getWorkInfosByTagLiveData
,而获取的也就是WorkInfo,也差不多。
val liveData = WorkManager.getInstance().getWorkInfosByTagLiveData("workRequest");
liveData.observe(this, Observer {
if (it[0].state.isFinished) {
// 任务已经完成
} else {
// 任务未完成
}
})
而获取任务结果的话 , 可以通过
WorkManager.getInstance(getContext()).getWorkInfoByIdLiveData(uuid)
.observe(getOwnerActivity(), new Observer() {
@Override
public void onChanged(WorkInfo workInfo) {
// 获取任务执行结果
workInfo.getOutputData();
}
});
取消任务
在WorkManager中也可以取消:
// 根据TAG取消所有任务
WorkManager.getInstance().cancelAllWorkByTag()
// 根据UniqueWorkName取消任务
WorkManager.getInstance().cancelUniqueWork()
// 根据uuid取消任务
WorkManager.getInstance().cancelWorkById()
增加任务约束
当某个任务需要在某个条件时开始,可以在任务的Builder
中添加约束(Constraint)。
// 创建正在充电的约束
val constraints = Constraints.Builder()
.setRequiresCharging(true)
.build()
// 将约束添加到请求中
val save = OneTimeWorkRequestBuilder()
.setConstraints(constraints)
.addTag(TAG_OUTPUT)
.build()
continuation = continuation.then(save)
// 启动任务
continuation.enqueue()
新增加进度同步
在最新的2.3.4
版本的WorkManager中新增任务进度同步的接口.
public class ProgressWorker extends Worker {
private static final String PROGRESS = "PROGRESS";
private static final long DELAY = 1000L;
public ProgressWorker(
@NonNull Context context,
@NonNull WorkerParameters parameters) {
super(context, parameters);
// 设置初始化进度为0
setProgressAsync(new Data.Builder().putInt(PROGRESS, 0).build());
}
@NonNull
@Override
public Result doWork() {
//Do something
...
// 设置进度为100
setProgressAsync(new Data.Builder().putInt(PROGRESS, 100).build());
return Result.success();
}
}
在LiveData
中获取到该进度
WorkManager.getInstance(getApplicationContext())
// requestId is the WorkRequest id
.getWorkInfoByIdLiveData(requestId)
.observe(lifecycleOwner, new Observer() {
@Override
public void onChanged(@Nullable WorkInfo workInfo) {
if (workInfo != null) {
Data progress = workInfo.getInt(PROGRESS, 0)
// Do something with progress
}
}
});