WorkManager 完全解析 + 重构轮询系统,android 事件分发机制面试
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 加入到 OneTi
meRequest.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();
3、多路径先后执行 上面两种方式都是单路径执行,可以实现更加复杂的多路径执行方式,使用WorkContinuation.combine(List<OneTimeWorkRequest>)
如下图要实现的执行方式:
WorkContinuation chain1 = WorkManager.getInstance().beginWith(workA).then(workB);WorkContinuation chain2 = WorkManager.getInstance().beginWith(workC).then(workD);WorkContinuation chain3 = WorkContinuation.combine(Arrays.asList(chain1, chain2)).then(workE);chain3.enqueue();
使用 WorkManager 遇到的问题
1、使用 PeriodicWorkRequest 只执行一次,并不重复执行。
WorkManager instance= new PeriodicWorkRequest.Builder(PollingWorker.class, 10, TimeUnit.MINUTES).build();
原因:PeriodicWorkRequest 默认的时间间隔是 15 分钟如果设置的时间小于 15 分钟,就会出现问题。
解决方法:设置的默认时间必须大于或等于 15 分钟。另外要注意,就算设置的时间为 15 分钟也不一定间隔 15 分钟就执行。所以要精确的间隔时间执行,一般不用 WorkManager,可以使用 AlarmManager 来实现。
2、在 doWork()方法中更新 UI 导致崩溃。 原因:doWork()方法是在 WorkManager 管理的后台线程中执行的,更新 UI 操作只能在主线程中进行。
解决方法:当 doWork()耗时方法执行完之后,将更新 UI 操作抛到主线中执行,可以用 handle 来实现,如下:
/**
Created time 15:32.
评论