Android11不光废弃了AsyncTask,还把IntentService一起废掉了,对于后台的异步服务,官方建议改为使用工作管理器WorkManager。
其实除了IntentService之外,Android也提供了其它后台任务工具,例如工作调度器JobScheduler、闹钟管理器AlarmManager等等。当然这些后台工具的用法各不相同,徒增开发者的学习时间而已,于是乎谷歌索性把它们统一起来,在Jetpack库中推出了工作管理器WorkManager。这个WorkManager的兼容性很强,对于Android6.0或更高版本的系统,它通过JobScheduler完成后台任务;对于Android6.0以下版本的系统(不含Android6.0),通过AlarmManager和广播接收器组合完成后台任务。不过无论采取哪种方案,后台任务最终都是由线程池Executor执行。
因为WorkManager来自Jetpack库,所以使用之前要修改build.gradle,增加下面一行依赖配置:
implementation 'androidx.work:work-runtime:2.4.0'
接着定义一个处理后台业务逻辑的工作者,该工作者继承自Worker抽象类,就像异步任务需要从IntentService派生而来那样。自定义的工作者必须实现构造方法,并重写doWork方法,其中构造方法可获得外部传来的请求数据,而doWork方法处理具体的业务逻辑。特别要注意,由于doWork方法运行于分线程,因此该方法内部不能操作界面控件。下面是自定义的工作者代码例子:
- public class CollectWork extends Worker {
- private final static String TAG = "CollectWork";
- private Data mInputData; // 工作者的输入数据
-
- public CollectWork(Context context, WorkerParameters workerParams) {
- super(context, workerParams);
- mInputData = workerParams.getInputData();
- }
-
- // doWork内部不能操纵界面控件
- @Override
- public Result doWork() {
- String desc = String.format("请求参数包括:姓名=%s,身高=%d,体重=%f",
- mInputData.getString("name"),
- mInputData.getInt("height", 0),
- mInputData.getDouble("weight", 0));
- Log.d(TAG, "doWork "+desc);
- // 这里填详细的业务逻辑代码
- Data outputData = new Data.Builder()
- .putInt("resultCode", 0)
- .putString("resultDesc", "处理成功")
- .build();
- return Result.success(outputData); // success表示成功,failure表示失败
- }
- }
然后在活动页面中构建并启动工作任务,详细过程主要分为下列四个步骤:
该步骤说明在哪些情况下才能执行后台任务,也就是运行后台任务的前提条件,此时用到了约束工具Constraints,约束条件的构建代码示例如下:
- // 1、构建约束条件
- Constraints constraints = new Constraints.Builder()
- //.setRequiresBatteryNotLow(true) // 设备电量充足
- //.setRequiresCharging(true) // 设备正在充电
- .setRequiredNetworkType(NetworkType.CONNECTED) // 已经连上网络
- .build();
该步骤把后台任务需要的输入参数封装到一个数据对象中,此时用到了数据工具Data,输入数据的构建代码示例如下:
- // 2、构建输入数据
- Data inputData = new Data.Builder()
- .putString("name", "小明")
- .putInt("height", 180)
- .putDouble("weight", 80)
- .build();
该步骤把约束条件、输入数据等请求内容组装起来,此时用到了工作请求工具OneTimeWorkRequest,工作请求的构建代码示例如下:
- // 3、构建一次性任务的工作请求
- String workTag = "OnceTag";
- OneTimeWorkRequest onceRequest = new OneTimeWorkRequest.Builder(CollectWork.class)
- .addTag(workTag) // 添加工作标签
- .setConstraints(constraints) // 设置触发条件
- .setInputData(inputData) // 设置输入参数
- .build();
- UUID workId = onceRequest.getId(); // 获取工作请求的编号
该步骤生成工作管理器实例,并将第3步的工作请求对象加入到管理器的执行队列,由管理器调度并执行请求任务,执行工作的代码例子如下所示:
- // 4、执行工作请求
- WorkManager workManager = WorkManager.getInstance(this);
- workManager.enqueue(onceRequest); // 将工作请求加入执行队列
当然,工作管理器不单拥有enqueue,还有其它的调度方法,常用的几个方法说明如下:
enqueue:将工作请求加入执行队列。
cancelWorkById:取消指定编号的工作。其中工作编号为第3步getId方法返回的workId。
cancelAllWorkByTag:取消指定标签的所有工作。其中工作标签为第3步设置的workTag。
cancelAllWork:取消所有工作。
getWorkInfoByIdLiveData:获取指定编号的工作信息。
鉴于后台任务是异步执行着的,若想知晓工作任务的处理结果,就得调用getWorkInfoByIdLiveData方法,获取工作信息并实时监听它的运行情况。此时工作结果的查询代码示例如下:
- // 获取指定编号的工作信息,并实时监听工作的处理结果
- workManager.getWorkInfoByIdLiveData(workId).observe(this, workInfo -> {
- if (workInfo.getState() == WorkInfo.State.SUCCEEDED) { // 工作处理成功
- Data outputData = workInfo.getOutputData(); // 获得工作信息的输出数据
- int resultCode = outputData.getInt("resultCode", 0);
- String resultDesc = outputData.getString("resultDesc");
- String desc = String.format("工作处理结果为:resultCode=%d,resultDesc=%s",
- resultCode, resultDesc);
- tv_result.setText(desc);
- }
- });
至此,工作管理器的任务操作步骤都过了一遍。眼尖的读者可能发现,第3步的工作请求类名叫做OneTimeWorkRequest,读起来像是一次性工作。其实工作管理器不仅支持设定一次性工作,也支持设定周期性工作,此时用到的工作请求名叫PeriodicWorkRequest,它的构建代码示例如下:
- // 3、构建周期性任务的工作请求。周期性任务的间隔时间不能小于15分钟
- String workTag = "PeriodTag";
- PeriodicWorkRequest periodRequest = new PeriodicWorkRequest.Builder(
- CollectWork.class, 15, TimeUnit.MINUTES)
- .addTag(workTag) // 添加工作标签
- .setConstraints(constraints) // 设置触发条件
- .setInputData(inputData) // 设置输入参数
- .build();
- UUID workId = periodRequest.getId(); // 获取工作请求的编号
最后在活动页面中集成工作管理器,运行测试App后点击启动按钮,观察到任务执行结果如下图所示,可见成功获知了后台工作的运行情况。