WorkManager 完全解析 + 重构轮询系统,大厂面试题汇总
WorkManager 主要类及使用
如下图给出了 WorkManager 中主要的类以及关系图,黄色区域是最主要的三个类,构成了 WorkManager 的基本框架,红色部分和绿色部分是关联的黄色部分的具体实现或者类里面包含一些规则或数据。
1、Worker 处理要执行的任务的具体逻辑。
2、WorkerRequest 代表一个独立的可以执行的任务,以及任务执行时的条件和规则,比如说任务执行一次还是多次以及任务的触发条件是什么任务有什么约束等。
3、WorkManager 提供队列将要执行的 WorkerRequest 放到队列中管理和执行。 如下图,三个主要类的关系:
下面分别介绍三个类的作用和使用方法。
Worker
Worker 是一个抽象类,当有一个要执行的任务的时候可以继承 Worker 类,重写 doWork()方法在 doWork()方法中实现具体任务的逻辑。
public class MyWorker extends Worker {public MyWorker(@NonNull Context appContext,@NonNull WorkerParameters workerParams) {super(appContext, workerParams);}@NonNull@Overridepublic Worker.Result doWork() {
Context applicationContext = getApplicationContext();
try {
Bitmap picture = BitmapFactory.decodeResource(applicationContext.getResources(),R.drawable.test);
return Worker.Result.SUCCESS;
} catch (Throwable throwable) {
return Worker.Result.FAILURE;
}}}
在上面的 MyWorker 实例中,继承了 Worker 并且重写了 doWork()方法,需要注意的是 doWork()方法是有返回值 Worker.Result 的,可以在任务执行成功是返回 Worker.Result.SUCCESS,在任务执行出现异常时返回 Worker.Result.FAILURE doWork()方法的返回值主要有三种 1、Worker.Result.SUCCESS 表示任务执行成功
2、Worker.Result.FAILURE 表示任务执行失败
3、Worker.Result.RETRY 通知 WorkManager 之后再尝试执行该任务
WorkRequest
WorkRequest 要指定执行任务的 Worker,也可以给 WorkRequest 加一些规则,比如说什么时候执行任务,任务执行一次还是多次,每一个 WorkRequest 都有一个自动产生的唯一 ID,可以根据唯一 ID 获取对应任务的状态以及是否取消对应的任务。如下图 WorkRequest 有两个实现类如下图:
1、OneTimeWorkRequest 任务只执行一次
OneTimeWorkRequest myWorkRequest =new OneTimeWorkRequest.Builder(MyWorker.class).build();//将上面定义的 MyWorker 加入到 OneTime
Request.Builder 方法中 WorkManager.getInstance().enqueue(myWorkRequest);//获取 WorkManager 实例并将 WorkRequest 进队
2、PeriodicWorkRequest PeriodicWorkRequest 重复执行任务,直到被取消才停止。首次执行是任务提交后立即执行或者满足所给的 Constraints 条件。以后执行都会根据所给的时间间隔来执行。注意任务的执行可能会有延时,因为 WorkManager 会根据 OS 的电量进行优化。 假如设置的 Periodic Work 是 24 小时执行一次,有可能根据电池优化策略执行的过程如下:
1 | Jan 01, 06:00 AM2 | Jan 02, 06:24 AM3 | Jan 03, 07:15 AM4 | Jan 04, 08:00 AM5 | Jan 05, 08:00 AM6 | Jan 06, 08:02 AM
由上面的执行时间可以看出,PeriodicWorkRequest 并不是准确的按照 24 小时来执行,会有一定的时间延迟。因此如果需要准确的间隔时间来执行任务的话不能使用 PeriodicWorkRequest。
Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();PeriodicWorkRequest build = new PeriodicWorkRequest.Builder(MyWorker.class, 25, TimeUnit.MILLISECONDS).addTag(TAG).setConstraints(constraints).build();
WorkManager instance = WorkManager.getInstance();if (instance != null) {instance.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, build);}
Constraints
可以给任务加一些运行的 Constraints 条件,比如说当设备空闲时或者正在充电或者连接 WiFi 时执行任务。
Constraints myConstraints = new Constraints.Builder().setRequiresDeviceIdle(true).setRequiresCharging(true)// Many other constraints are available, see the// Constraints.Builder reference.build();OneTimeWorkRequest myWork =new OneTimeWorkRequest.Builder(CompressWorker.class).setConstraints(myConstraints).build();
WorkManager
WorkManager 管理 WorkRequest 队列。并根据设备和其他条件选择执行的具体方式。在大部分情况在如果没有给队列加 Contraints,WorkManager 会立即执行任务。
WorkManager.getInstance().enqueue(myWork);
如果要检查任务的执行状态可以通过获取 WorkInfo,WorkInfo 在 WorkManager 里面的LiveData<WorkInfo>
中。下面是判断任务是否结束的方式。
WorkManager.getInstance().getWorkInfoByIdLiveData(myWork.getId()).observe(lifecycleOwner, workInfo -> {// Do something with the statusif (workInfo != null && workInfo.getState().isFinished()) {// ...}});
取消任务执行
通过任务的 ID 可以获取任务从而取消任务。任务 ID 可以从 WorkRequest 中获取。
UUID compressionWorkId = compressionWork.getId();WorkManager.getInstance().cancelWorkById(compressionWorkId);
注意并不是所有的任务都可以取消,当任务正在执行时是不能取消的,当然任务执行完成了,取消也是意义的,也就是说当任务加入到 ManagerWork 的队列中但是还没有执行时才可以取消。
WorkManager 多任务调度
有时候可能有很多任务需要执行,并且这些任务之前可能有先后顺序或者某些依赖关系,WorkManager 提供了很好的方式。 1、先后顺序执行单个任务 比如说有三个任务 workA,workB,workC,并且执行顺序只能时 workA---->workB---->workC 可以用如下的方式处理。
WorkManager.getInstance().beginWith(workA).then(workB) instance.then(workC).enqueue();
上面的 workA,workB,workC,都是 WorkRequest 的子类实现对象。WorkManager 会根据上面的先后顺序来执行 workA,workB,workC,,但是如果执行过程中三个任务中有一个失败,整个执行都会结束。并且返回Result.failure()
。 2、先后顺序执行多个任务列 有时候可能要先执行一组任务,然后再执行下一组任务,可以使用下面的方式来完成。
WorkManager.getInstance()// First, run all the A tasks (in parallel):.beginWith(Arrays.asList(workA1, workA2, workA3))// ...when all A tasks are finished, run the single B task:.then(workB)// ...then run the C tasks (in any order):.then(Arrays.asList(workC1, workC2)).enqueue();
评论