一、WorkManager 的介绍
WorkManager 是用来执行后台任务的,例如,发送应用程序日志、同步应用程序数据、备份用户数据等。WorkManager 能保证任务一定会被执行,即使应用程序当前不在运行中,甚至在设备重启过后,任务仍然会在适当的时刻被执行。
这是因为 WorkManager 有自己的数据库,关于任务的所有信息和数据都保存在该数据库中。因此,只要任务交给了 WorkManager ,哪怕应用程序彻底退出,或者设备被重新启动,WorkManager 依然能够保证完成你交给它的任务。WorkManager 最低能兼容 API Level 14,并且不需要你的设备安装 Google Play Services。因此,不用过于担心兼容性问题,因为 API Level 14 已经能够兼容几乎 100% 的设备。
二、WorkManager 的使用
依赖如下:
implementation "androidx.work:work-runtime:2.6.0"
注意:WorkManager 需要 compileSdk
版本 28 或更高版本。
1、 创建简单的任务
这里我们先学习如何创建一次性执行的任务,首先我们需要继承 Worker 并实现 doWork() 方法,所执行的任务就在这个方法中执行,在任务中我们打印一句话,代码如下:
public class SimpleWork extends Worker { public SimpleWork(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { Log.d("lx-test", "简单任务,执行完毕,doWork 线程:" + Process.myTid()); return Result.success(); } }
doWork()方法的返回值类型为Result,它有三种状态:
Result.success() :执行成功。
Result.failure() :执行失败。
Result.retry() :需要重新执行。
在 Activity 中执行任务的代码如下,其中 OneTimeWorkRequest 是只执行一次的任务请求继承自 WorkRequest
WorkRequest 代表一个独立的可以执行的任务,以及任务执行的条件和规则,比我说任务执行一次还是多次,以及任务的触发调试是什么,任务有什么约束等。
WorkManager 提供队列将要执行的 WorkManager 放到队列中管理和执行。
流程如下:
/** * 简单任务 * * @param view */ public void simpleWork(View view) { Log.d("lx-test","主线程:" + Process.myTid()); OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(SimpleWork.class) /** * 给任务添加 Tag,后面可以根据 Tag取消任务 * 不添加会默认用 包名+SimpleWork */ .addTag("task") .build(); WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); }
执行结果如下:
通过上面的结果可以知道,doWork 方法并不是在主线程中执行的,而是在 WorkManager 管理的后台线程中执行的,因此不可以在 doWork 方法中更新 UI 。
另外我们还可以给任务添加延时执行,如下所示,通过 setInitialDelay() 方法将任务的执行延迟2秒。
/** * 简单任务 * * @param view */ public void simpleWork(View view) { OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(SimpleWork.class) .setInitialDelay(2, TimeUnit.SECONDS) .build(); WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); }
可以给任务加一些运行的 Constraints 条件,比如说当设备空闲时或者正在充电或者连接 WiFi 时执行任务。
/** * 简单任务 * * @param view */ public void simpleWork(View view) { Constraints constraints = new Constraints.Builder() .setRequiresDeviceIdle(true) // 设备空闲 .setRequiresCharging(true) // 设备充电 .build(); OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(SimpleWork.class) .setConstraints(constraints) .build(); WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); }
2、Worker的数据传递
数据我们使用 Data 来进行传递,Activity 中向 Worker 传递数据通过 setInputData() 方法,并且我们在这里也监听了 Worker 回传的数据,代码如下所示:
public class TransferWork extends Worker { public TransferWork(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { String inputData = getInputData().getString("data"); Log.d("lx-test", "Work接收到的数据:" + inputData + ",doWork 线程:" + Process.myTid()); Data data = new Data.Builder() .putString("data", "Worker的数据").build(); return Result.success(data); } }
在Activity中执行任务的代码如下:
/** * 数据传递 * @param view */ public void transferData(View view) { Log.d("lx-test","主线程:" + Process.myTid()); Data data = new Data.Builder() .putString("data", "Activity的数据") .build(); OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(TransferWork.class) .setInputData(data) .build(); WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); WorkManager.getInstance(this) .getWorkInfoByIdLiveData(oneTimeWorkRequest.getId()) .observe(this, new Observer<WorkInfo>() { @Override public void onChanged(WorkInfo workInfo) { if (workInfo.getState().isFinished()) { Data outputData = workInfo.getOutputData(); Log.d("lx-test", "WorkManger回传的数据:" + outputData.getString("data") + ", onChanged 线程:" + Process.myTid()); } } }); }
执行结果如下:
通过上面的结果可以知道,onChanged 方法是执行在主线程中的,可以直接更新 UI 。
3、任务链
这里我们可以使用 WorkManager 的 beginWith() 和 then() 方法设置先执行的任务和后执行的任务,beginWith() 和 then() 方法也都可以传递任务集合。
public class MultiWork extends Worker { public MultiWork(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { String inputData = getInputData().getString("data"); Log.d("lx-test", "任务链接收到的数据:" + inputData); return Result.success(); } }
在Activity中执行任务的代码如下:
/** * 任务链 * @param view */ public void multiWork(View view) { List<OneTimeWorkRequest> workRequestList = new ArrayList<>(); OneTimeWorkRequest oneTimeWorkRequest1 = new OneTimeWorkRequest.Builder(MultiWork.class) .setInputData(new Data.Builder().putString("data","任务1").build()) .build(); OneTimeWorkRequest oneTimeWorkRequest2 = new OneTimeWorkRequest.Builder(MultiWork.class) .setInputData(new Data.Builder().putString("data","任务2").build()) .build(); OneTimeWorkRequest oneTimeWorkRequest3 = new OneTimeWorkRequest.Builder(MultiWork.class) .setInputData(new Data.Builder().putString("data","任务3").build()) .build(); workRequestList.add(oneTimeWorkRequest1); workRequestList.add(oneTimeWorkRequest2); WorkManager.getInstance(this) .beginWith(oneTimeWorkRequest3) .then(workRequestList)// 集合中的任务按照添加的先后顺序依次执行 .enqueue(); }
4、周期性任务
前面提到过的 OneTimeWorkRequest 是一次性任务。一次性任务在任务成功执行后,便彻底结束。而周期性任务则需要通过 PeriodicWorkRequest 来实现,它会按照设定的时间定期执行。二者使用起来没有太大差别。需要注意的是,周期性任务的间隔时间不能少于15分钟,具体代码如下:
public class PeriodicWork extends Worker { public PeriodicWork(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { Log.d("lx-test","周期性任务"); return Result.success(); } }
在Activity中执行任务的代码如下:
/** * 周期任务 * @param view */ public void periodicWork(View view) { PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest .Builder(PeriodicWork.class,15,TimeUnit.MINUTES) .build(); WorkManager.getInstance(this) .enqueue(periodicWorkRequest); }
特别说明:
-
PeriodicWorkRequest 重复执行任务,直到被取消才停止。首次执行是任务提交后立即执行或者满足所给的 Constraints 条件。以后执行都会根据所给的时间间隔来执行。注意任务的执行可能会有延时,因为WorkManager 会根据 OS 的电量进行优化。因此如果需要准确的间隔时间来执行任务的话不能使用PeriodicWorkRequest。
-
PeriodicWorkRequest 默认的时间间隔是15分钟如果设置的时间小于15分钟,就会出现 PeriodicWorkRequest只执行一次,并不重复执行。
5、取消任务
//根据id取消 WorkManager.getInstance(this).cancelWorkById(oneTimeWorkRequest1.getId()); //取消所有 WorkManager.getInstance(this).cancelAllWork(); //根据Tag取消 WorkManager.getInstance(this).cancelAllWorkByTag("Tag");