WorkManager可以用来执行一个在特定条件下触发执行的延时任务。比如我设置触发条件是,手机有网络连接的时候才会触发。当我们断网的时候打开app,我们的任务不会立即执行,当手机联网之后才会触发。
这样我们就可以利用WorkManager轻松实现某些条件下触发的延时任务。
引入
在项目中引入WorkManager包
implementation "android.arch.work:work-runtime:1.0.0-alpha01"
implementation "android.arch.work:work-firebase:1.0.0-alpha01"
使用
首先我们要创建我们的任务类。
ublic class MyWork extends Worker {
@NonNull
@Override
public WorkerResult doWork() {
//执行任务
return WorkerResult.SUCCESS;
}
}
doWork会在条件满足的时候触发。
再来看看怎么定义触发规则并把任务添加到WorkManager中。
Constraints constraints = new Constraints.Builder()//定义触发规则
.setRequiredNetworkType(NetworkType.CONNECTED)//触发规则是当有络连接的时候执行
.build();
Data data = new Data.Builder()//添加数据
.putString("data","你好")
.build();
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWork.class)//创建WorkRequest
.setConstraints(constraints)
.setInputData(data)
.build();
WorkManager.getInstance().enqueue(workRequest);//添加到WorkManager去管理任务。
Constraints
用来定义任务的触发条件,网络状态说明:
状态 | 说明 |
---|---|
NOT_REQUIRED | 没有要求 |
CONNECTED | 网络连接 |
UNMETERED | 连接无限流量的网络 |
METERED | 连接按流量计费的网络 |
NOT_ROAMING | 连接非漫游网络 |
除了网络连接的状态还有下面的触发条件可以选择:
setRequiresBatteryNotLow(true)//不在电量不足时执行
setRequiresCharging(true)//在充电时执行
setRequiresStorageNotLow(true)//不在存储容量不足时执行
setRequiresDeviceIdle(true)//在待机状态执行
Data
用来创建要传递给worker的参数,传入方式是键值对。
OneTimeWorkRequest
这个Request说明只会触发一次。如果需要执行周期性任务可以使用PeriodicWorkRequest。
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest
.Builder(MyWorker.class,15, TimeUnit.SECONDS)
.setConstraints(constraints)
.setInputData(data)
.build();
取消任务
UUID workId = workRequest.getId();
WorkManager.getInstance().cancelByWorkId(workId);
Worker接收并返回参数
public class MyWork extends Worker {
@NonNull
@Override
public WorkerResult doWork() {
String name = getInputData().getString("data", "");//获取数据
//做些什么
Data data = new Data.Builder().putString("out","返回点什么").build();
setOutputData(data);//返回数据
return WorkerResult.SUCCESS;
}
}
拿到worker返回的数据
要拿到返回的数据需要利用回调。
WorkManager.getInstance().getStatusById(workRequest.getId())//通过workRequest的id获取
.observe(this, new Observer() {
@Override
public void onChanged(@Nullable WorkStatus workStatus) {
String data = workStatus.getOutputData().getString("out","");
Log.d("MainActivity", data);
}
});
第一个参数是LifecycleOwner,回调同步生命周期,是为了防止我们页面退出之后可能造成的内存泄漏。
我们从第二个回调里面可以拿到返回的数据。
在这里我们要着重注意数据的大小,如果数据太大就会报下面的异常:
对,添加到Data的数据不能大于10KB,本来我也以为是10M(火星换算法10240KB=10KB),但是我的数据才十几KB,怎么会超出范围,然后从源码中发现:
可能作者跟大家开了个玩笑。大家跟着笑就行了。哈哈
链式任务
有些时候任务执行前后顺序会影响到结果,需要顺序执行某些任务,又或者后面的任务需要使用前面任务得出的结果,这个时候我们就可以使用链式任务。
1、ABC先后执行
WorkManager.getInstance().beginWith(A).then(B).then(C).enqueue();
2、A,B没有先后顺序,但是A、B均为C的前驱任务
WorkManager.getInstance().beginWith(A,B).then(C).enqueue();
3、A、B和C、D链式执行,执行完之后在执行E
WorkContinuation workContinuation1 = WorkManager.getInstance().beginWith(A).then(B);
WorkContinuation workContinuation2 = WorkManager.getInstance().beginWith(C).then(D);
WorkContinuation workContinuation3 = WorkContinuation.combine(workContinuation1,workContinuation2).then(E).enqueue();
强大的生命力
WorkManager有着强大的存活能力,当我们断网的时候打开app,这个时候任务没有满足触发条件,退出app(进程没有被清除)。这时候连上网络依然会执行任务。
但是当我们结束进程就会出现下面的情况。
我们进行下面的操作。
1、断开网络
2、打开app,这个时候不会执行
3、杀掉应用进程
4、打开网络,第一次的任务没有执行
5、打开app
然后会发现,任务执行了两次。第一次的执行是第一次运行后,加入了任务队列,但还没有执行的任务。
第二次执行是我们第二次打开,因为满足网络连接条件立即执行。
这是 WorkManager 的另一个特点,一旦发起一个任务,任务是可以保证一定会被执行的,就算退出应用,甚至重启手机都阻止不了他。但可能由于添加了环境约束等原因,它执行的时间是不确定的。
原理我们利用网上的一个图片说明。
当应用正在运行时,它会在当前的进程中启用一个子线程执行。应用没有运行的情况下启用,它则会自己选择一种合适的方式在后台运行。具体是什么方式和 Android 的版本和依赖环境有关:
是不是很强大
拜拜~~