WorkManager
WorkManager
作用
管理后台任务,平衡用电量和用户体验
1.针对的是不需要及时完成的任务。
例如,发送应用程序日志、同步应用程序数据、备份用户数据等,站在业务需求的角度,这些任务都不需要立即完成。如果我们自己来管理这些任务,逻辑可能会非常复杂,若API 使用不恰当,可能会消耗大量电量。
- 保证任务一定会被执行。
WorkManager 能保证任务一定会被执行,即使应用程序当前不在运行中,甚至在设备重启过后,任务仍然会在适当的时刻被执行。这是因为 WorkManager 有 自己的数据库,关于任务的所有信息和数据都保存在该数据库中。因此,只要任务交给了 WorkManager, 哪怕应用程序彻底退出,或者设备被重新启动, WorkManager 依然能够保证完成你交给它的任务。
- 兼容范围广
WorkManager 最低能兼容API Level 14,并且不需要你的设备安装Google Play Services 。因此,不用过于担心兼容性问题,因为API Level 14已经能够兼容几乎 100%的设备。
WorkManager 能依据设备的情况,选择不同的执行方案。在API Level 23以上 的设备中,通过 JobScheduler 完成任务;在 API Level 23 以下的设备中,通过 AlarmManager 和 Broadcast Receivers 组合来完成任务。但无论采用哪种方案,任 务最终都是交由Executor 来执行的
使用
使用Worker类定义任务
假设要实现将日志上传到服务器的需求。新建一个名为 UploadLogWorker 的 类,该类继承自Worker 类,重写doWork()方法
doWork()方法有3种类型的返回值。
● 若执行成功,则返回Result.success()。
● 若执行失败 , 则返回Result.failure()。
● 若需要重新执行,则返回Result.retry()。
使用WorkReques配置任务
-
设置任务触发条件。
Constraints constraints = new Constraints.Builder().setRequiresCharging(true).setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresBatteryNotLow(true).build();
-
将任务触发条件设置到WorkRequest。
WorkRequest 是一个抽象类,它有两种实现方式——OneTimeWorkRequest 和 PeriodicWorkRequest, 分别对应的是一次性任务和周期性任务,周期性任务的间隔时间不能少于15分钟。
假设没有设置触发条件,或者所设置的触发条件此刻符合系统的执行要求, 此时,系统有可能会立刻执行该任务。但如果你希望能够延迟任务的执行,那么 可 以 通 过setInitialDelay() 方法 , 对任 务 进 行 延 后
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadLogWorker.class) .setConstraints(constraints).setInitialDelay(10,TimeUnit.SECONDS).build();
3.假如 Worker 线程的执行出现了异常,如服务器宕机。你可能希望过一段时间 后,重试该任务。那么可以在Worker 的 doWork() 方法中返回Result.retry(), 系 统 会有默认的指数退避策略来帮你重试任务,也可以通过setBackoffCriteria() 方法来 自定义指数退避策略 。
OneTimeWorkRequest uploadWorkRequest =new OneTimeWorkRequest.Builder(UploadLogWorker.class)//设置指数退避算法.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MILLISECONDS) .build();
4.设置 tag 标签后,你就可以通过该标签跟踪任务的状态:WorkManager.getWork InfosByTagLiveData(String tag); 也 可 以 取 消 任 务 :WorkManager.cancelAll WorkByTag(String tag)。
OneTimeWorkRequest uploadWorkRequest =new OneTimeWorkRequest.Builder(UploadLogWorker.class).addTag("UploadTag").build();
5.将任务提交给系统
WorkManager.getInstance(this).enqueue(uploadWorkRequest);
观察任务状态
任务在提交给系统后,可以通过WorkInfo 获知任务的状态。WorkInfo 包含任 务的id 、tag 、Worker 对象传递过来的outputData, 以及任务当前的状态。有3种 方式可以得到WorkInfo 对象。
● WorkManager.getWorkInfosByTag()。
● WorkManager.getWorkInfoById()。
● WorkManager.getWorkInfosForUnique Work()。
如果希望实时获知任务的状态,这3个方法还有对应的LiveData 方法。
● WorkManager.getWorkInfosByTagLiveData()。
● WorkManager.getWorkInfoByIdLiveData()。
● WorkManager.getWorkInfosForUniqueWorkLiveData()。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(uploadWorkRequest.getId()).observe(MainActivity.this,new Observer<WorkInfo>(){@Overridepublic void onChanged(WorkInfo workInfo){Log.d("onChanged()->","workInfo:"+workInfo);});
取消任务
与观察任务类似,我们也可以根据 id 或 tag 取消某个任务,或者取消所有的任务。
WorkManager.getInstance(MainActivity.this).cancelA11Work();
WorkManager 与Worker 之间的参数传递
WorkManager 通过 setInputData() 方法向 Worker 传递数据。数据的传递通过 Data 对象来完成。需要注意的是, Data 只能用于传递一些小的基本类型的数据, 且数据最大不能超过10KB。
Data inputData = new Data.Builder().putString("input_data","Hello World!").build();
OneTimeWorkRequest uploadworkRequest =new OneTimeworkRequest.Builder(UploadLogWorker.class).setInputData(inputData).build();
Worker 通 过getInputData() 方法接收数据,并在任务完成后,向WorkManager 返回数据 。
@Override
public Result dowork()
{
// 接收外面传递进来的数据
String inputData = getInputData()·getString("input_data");
//任务执行完成后返回数据
Data outputData = new Data.Builder().putString("output_data","Task Success!").build();
}
WorkManager 通过LiveData 得到从Worker 返回的数据。
WorkManager.getInstance(this)
.getWorkInfoByIdLiveData(uploadWorkRequest.getId())
.observe(MainActivity.this,new Observer<WorkInfo>()
{@Overridepublic void onChanged(WorkInfo workInfo){if(workInfo !=null && workInfo.getState()==WorkInfo.State.SUCCEEDED){String outputData = workInfo.getOutputData().getString("output_data");}}
});
任务链
如果有一系列的任务需要按顺序执行,那么可以利用 WorkManager.begin With().then().then().…enqueue()的方式构建任务链。例如,在上传数据之前,可能需 要先对数据进行压缩。
WorkManager.getInstance(this)
.beginWith(compressWorkRequest)
.then(uploadWorkRequest)
.enqueue();
假设有更复杂的任务链,那么还可以考虑使用 WorkContinuation.combine()方 法,将任务链组合起来使用。在下面的代码中,任务的执行顺序为A 、B 、
E
C 、D、
WorkContinuation workContinuation1 = WorkManager.getInstance(this).beginWith(WorkRequestA).then(WorkRequestB);
WorkContinuation workContinuation2 =WorkManager.getInstance(this).beginWith(WorkRequestC).then(WorkRequestD);
List<WorkContinuation>taskList=new ArrayList<>();
taskList.add(workContinuation1);
taskList.add(workContinuation2);
WorkContinuation.combine(taskList)
.then(WorkRequestE) .enqueue();
注意点
1.虽然 WorkManager 宣称,能够保证任务得到执行,在非 Android 原生 系统的真实设备中进行测试时发现,应用程序彻底退出与设备重新启动之后,任务都没有被再次执行。
前面也提到 了 ,WorkManager 会根据系统的版本,决定采用 JobScheduler 或 AlarmManager + Broadcast Receivers来完成任务。问题在于,这些API 很可能会受 到非原生系统的影响。例如,某个非原生系统如果不允许AlarmManager 被主动唤 起,那么 WorkManager很可能无法在该系统上正常使用。
2.周期性任务的实际执行时间,与所设定的时间差别较大。并且在任务执行完成后WorkInfo 并不会收到 Success的通知。查阅相关资料后发现,Android认为Success 和 Failure 都属于“终止类”的通知。这意味着,若发出此类通知,则表明任务“彻底终止”, 而周期性任务是不会彻底终止的,它会一直执行下去。因此,在使用 LiveData 观 察周期性任务时,不会收到Success 这一类的通知