导语
Jetpack简介及其它组件文章
还在为发布动态流程过于复杂而心烦吗?还在抱怨弱网关注失败吗?还在担心重要任务丢失吗?不用担心,WorkManager帮你解决这一切。
主要内容
- 什么是WorkManager
- 简单使用WorkManager
- WorkManager的使用场景
- WorkManager的原理解析
具体内容
什么是WorkManager
WorkManager可以自动维护后台任务的执行时机,执行顺序,执行状态。
执行时机(带约束条件的任务)
创建任务的执行时机,立刻执行还是设备空闲的时候执行,或者设备的充电量满足的时候执行,或者在Wifi下才会执行,或者是在设备存储空间足够的情况下才能执行。
执行顺序
任务执行可以有依赖关系,比如执行完任务A才能执行任务B,任务B执行完才能执行任务C。
执行状态
任务执行的每一个状态都会回调给我们,成功,取消,失败。
特点
- 保证任务一定会被执行。
- 合理使用设备资源。
与Service对比
- WorkManager同样是可以应用于异步任务的并发场景的。
- WorkManager更轻便,更加省电。理论上Service能做的事情,WorkManager都可以做。Android8.0以后,Android对于后台service管理的更加严格,应用在后台启动的服务必须是前台服务,否则会导致应用crash。
- WorkManager是支持多任务的链式调用,支持多任务的串型依赖,还支持和LiveData相搭配使用,所以很容易观察任务执行的如何。
任务场景
WorkContinuation left, right;
left = workManager.beginWith(A).then(B);
right = workManager.beginWith(C).then(D);
WorkContinuation.combine(Arrays.asList(left, right)).then(E).Enqueue();
任务状态
- BLOCKED:表示当前有尚未完成的前提性工作
- ENQUEUED:表示工作能够在满足约束和时机条件后可立即执行
- RUNNING:表示工作器在活跃的执行
- SUCCEEDED:工作期返回成功的状态,此时是一种终止的状态,且只有OneTimeWorkRequest可以进入这种状态
- CANCELLED:当明确取消尚未终止的WorkRequest时,它会进入CANCELLED状态,所有依赖工作也会被标记为CANCELLED状态,且不会运行
- FAILED:工作期返回失败的状态,此时是一种终止的状态,所有依赖的工作也会被标记为FAILED状态,且只有OneTimeWorkRequest可以进入这种状态
任务控制
- cancelWorkById(UUID)——通过ID取消单个任务。
- cancelAllWorkByTag(String)——通过Tag取消所有任务。
- cancelUniqueWork(String)——通过名字取消唯一任务。
类关系
- Worker:任务的执行者,是一个抽象类,需要继承实现要执行的任务。
- WorkRequest:指定让哪个Woker执行任务,指定执行的环境,执行的顺序等,要使用它的子类OneTimeWorkRequest或PeriodicWorkRequest。
- WorkManager:管理任务请求和任务队列,发起WorkRequest会进入它的任务队列。
- WorkStatus:包含有任务的状态和任务信息,以LiveData的形式提供给观察者。
简单使用WorkManager
创建FollowWorker类,doWork() 方法是具体要执行的任务。
public class FollowWorker extends Worker {
public FollowWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.d("WorkManager", "开始关注");
Data inputData = getInputData();
String uid = inputData.getString("uid");
if (TextUtils.isEmpty(uid)) {
return Result.failure();
} else {
//假装这里是Http同步请求
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String relationship = CommonConstants.RELATIONSHIP.FOLLOWED_HIM;
Data outputData = new Data.Builder()
.putString("relationship", relationship)
.build();
Log.d("WorkManager", "关注成功");
return Result.success(outputData);
}
}
}
在需要关注的地方使用FollowWork。
private UUID followUUID;
private void toFollow(String uid) {
OneTimeWorkRequest request = getOneTimeWorkRequest(uid);
followUUID = request.getId();
enqueue(request);
}
private OneTimeWorkRequest getOneTimeWorkRequest(String uid) {
Data inputData = new Data.Builder()
.putString("uid", uid)
.build();
// @SuppressLint("RestrictedApi") Constraints constraints = new Constraints();
// //设备存储空间充足的时候 才能执行 ,>15%
// constraints.setRequiresStorageNotLow(true);
// //必须在执行的网络条件下才能好执行,不计流量 ,wifi
// constraints.setRequiredNetworkType(NetworkType.UNMETERED);
// //设备的充电量充足的才能执行 >15%
// constraints.setRequiresBatteryNotLow(true);
// //只有设备在充电的情况下 才能允许执行
// constraints.setRequiresCharging(true);
// //只有设备在空闲的情况下才能被执行 比如息屏,cpu利用率不高
// constraints.setRequiresDeviceIdle(true);
// //workmanager利用contentObserver监控传递进来的这个uri对应的内容是否发生变化,当且仅当它发生变化了
// //我们的任务才会被触发执行,以下三个api是关联的
// constraints.setContentUriTriggers(null);
// //设置从content变化到被执行中间的延迟时间,如果在这期间。content发生了变化,延迟时间会被重新计算
//这个content就是指 我们设置的setContentUriTriggers uri对应的内容
// constraints.setTriggerContentUpdateDelay(0);
// //设置从content变化到被执行中间的最大延迟时间
//这个content就是指 我们设置的setContentUriTriggers uri对应的内容
// constraints.setTriggerMaxContentDelay(0);
OneTimeWorkRequest request = new OneTimeWorkRequest
.Builder(FollowWorker.class)
.setInputData(inputData)
// .setConstraints(constraints)
// //设置一个拦截器,在任务执行之前 可以做一次拦截,去修改入参的数据然后返回新的数据交由worker使用
// .setInputMerger(null)
// //任务被调度执行的延迟时间
// .setInitialDelay(10, TimeUnit.SECONDS)
// //当一个任务被调度失败后,所要采取的重试策略,可以通过BackoffPolicy来执行具体的策略 线性、指数性
// .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
// //设置该任务尝试执行的最大次数
// .setInitialRunAttemptCount(2)
// //设置这个任务开始执行的时间
// //System.currentTimeMillis()
// .setPeriodStartTime(0, TimeUnit.SECONDS)
// //指定该任务被调度的时间 同上
// .setScheduleRequestedAt(0, TimeUnit.SECONDS)
// //当一个任务执行状态变成finish时,又没有后续的观察者来消费这个结果,那么workamnager会在
// //内存中保留一段时间的该任务的结果。超过这个时间,这个结果就会被存储到数据库中
// //下次想要查询该任务的结果时,会触发workmanager的数据库查询操作,可以通过uuid来查询任务的状态
// .keepResultsForAtLeast(10, TimeUnit.SECONDS)
.build();
return request;
}
private void enqueue(OneTimeWorkRequest workRequests) {
// List requests = new ArrayList<>();
WorkContinuation workContinuation = WorkManager.getInstance(getContext()).beginWith(workRequests);
workContinuation.enqueue();
workContinuation.getWorkInfosLiveData().observe(getViewLifecycleOwner(), new Observer>() {
@Override
public void onChanged(List workInfos) {
//block runing enuqued failed susscess finish
for (WorkInfo workInfo : workInfos) {
WorkInfo.State state = workInfo.getState();
Data outputData = workInfo.getOutputData();
UUID uuid = workInfo.getId();
if (state == WorkInfo.State.FAILED) {
// if (uuid==coverUploadUUID)是错的
if (uuid.equals(followUUID)) {
Log.d("WorkManager", "回调关注失败");
}
} else if (state == WorkInfo.State.SUCCEEDED) {
Log.d("WorkManager", "回调关注成功");
String relationship = outputData.getString("relationship");
if (uuid.equals(followUUID)) {
Log.d("WorkManager", "回调关注结果 = " + relationship);
}
}
}
}
});
}
WorkManager的使用场景
- 关注用户,加入基地等用户重要操作,不想被丢失。
- 发布动态,发布帖子等串行并行操作。
- 抛埋点,提升埋点准确率。
- 开机视频广告在wifi下预加载。
- 开机启动优化。
- 心跳请求。
WorkManager的原理解析
WorkManager的初始化
- WorkManager的初始化是在app冷启动后,由
WorkManagerInitializer
这个ContentProvider
执行的。 - 初始化过程包含了
Configuration
,WorkManagerTaskExecutor
,WorkDatabase
,Schedulers
,Processor
等的初始化过程。 - Schedulers有两个。
- (1) GreedyScheduler:执行没有任何约束的非周期性的任务。
- (2) SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler:执行周期性或者有约束性的任务。优先返回
SystemJobScheduler
,在build version
小于23的情况下先尝试返回GcmBasedScheduler
,若返回为空再返回SystemAlarmScheduler
。
- 初始化的最后,会根据情况找到需要被执行的任务进行调度执行。
WorkRequest的创建
WorkRequest的创建是为了持有三个重要的成员变量。分别是:
- mId:由UUID生成的任务id。
- mWorkSpec:每个任务的属性。
- mTags:每个任务的标签。
非约束条件任务的执行过程
- 在
WorkManager
执行了enqueue()
后,创建WorkContinuationImpl
对象执行enqueue()
方法。 -
WorkContinuationImpl
持有的EnqueueRunnable
对象将任务添加到db
,并交给Schedulers
去调度。 -
Schedulers
将任务交给每一个Scheduler
去处理。在我们的示例中,GreedyScheduler
会先处理这个任务。 -
GreedyScheduler
经过一系列判断后,调用WorkManager
的startWork()
方法执行这种一次性,非延迟,无约束的任务。 -
WorkManager
持有的StartWorkRunnable
对象会将任务交给Processor
去处理,执行startWork()
方法。 -
Processor
创建一个WorkerWrapper
对象,由它去调用Worker的startWork()
方法,执行我们自定义worker
的任务,并返回相应的result
。 - 任务完成后,
WorkerWrapper
会根据result
对任务状态,db
等进行更新,然后schedule
下一个任务。
带约束的任务的执行过程
- 创建JobService。
- 配置JobInfo。
- 执行。
更多内容戳这里(整理好的各种文集)