JobScheduler的基本使用
JobScheduler的使用分为:
- 创建一个JobService的子类,用于执行后台任务。
- 获取系统服务JobScheduler。
- 构建JobInfo实例,指定自定义JobService的子类的约束条件。
- 通过JobScheduler(schedule 接口)加入到任务队列,系统开始调度。
基本使用代码如下:
自定义JobService,执行后台任务
public SimpleService extends JobService{
public boolean onStartJob(JobParameters params) {
// 在UI线程中执行。
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
// 在UI线程中执行。
Log.d(TAG,"onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
return true;
}
}
通过JobScheduler进行系统调度
private void startWorkJobService(){
// 获取系统服务 JobScheduler
JobScheduler scheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (scheduler == null){
return;
}
// 获取系统中的调度任务
List<JobInfo> jobInfoList = scheduler.getAllPendingJobs();
for (JobInfo jobInfo : jobInfoList){
if (jobInfo.getId() == ThirdService.JOB_ID){
scheduler.cancel(ThirdService.JOB_ID);
}
}
ComponentName jobService = new ComponentName(getContext(), ThirdService.class);
JobInfo.Builder builder = new JobInfo.Builder(ThirdService.JOB_ID, jobService);
JobInfo jobInfo = builder
.setPeriodic(3*1000)
.build();
scheduler.schedule(jobInfo);
System.out.println("开始执行work job "+jobInfo.isPeriodic());
}
JobScheduler 注意事项
- JobScheduler 在不同的版本,其接口是不一样的,因此在使用时,需要特别注意版本兼容问题。
- JobScheduler 使用的步骤非常简单,但是其中涉及到不少的注意事项,下面着重记录JobScheduler的使用过程中的注意事项。
JobService 子类的注意事项
继承JobService,必须要实现onStartJob
,‘onStopJob’。简要代码如下:
public SimpleService extends JobService{
public boolean onStartJob(JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG,"onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
return true;
}
}
JobService 子类 注意事项一
JobService 子类的方法’onStartJob’,‘onStopJob’ 是运行在UI主线程,因此需要注意的是两个函数都不能执行耗时操作,如果需要执行耗时操作,需要单独创建线程。
JobService 子类 注意事项
onStartJob
是在后台任务开始时调用这个方法。
- 返回值为
true
,表明onStartJob
,执行到return
语句时,该任务已执行完成,通知系统进行下一次的调度。由于’onStartJob’在UI线程,因此该任务不能是耗时任务。 - 返回值为
false
,表明’onStartJob’,执行到return
语句时,该任务未执行完成,一般这时候是耗时任务,通过线程中执行任务,此时,任务执行完成,需要调用接口jobFinished()
,表明任务执行完成,通知系统进行下一次的调度。 onStartJob
入参JobParameters
,在构建JobInfo时会把参数通过Parameters传递给onStartJob
,比如jobId,序列化参数PersistableBundle
, 过程参数Bundle
等等。
onStopJob
系统决定停止当前正在执行的后台任务时调用。
- 调用onStopJob的时机,有两种情况:情况一,任务正在执行,且主动调用JobScheduler的cancel接口。情况二,系统主动停止调度。代码中注释中说如果不满足约束条件(网络条件,电量条件等)会触发,但是经过测试,网络条件改变并不会触发次接口,后面有机会可以进行多项测试确定次问题。
- 返回值为true,表明此项任务会通过重试策略再次执行。返回值为false,表明任务执行完成,会根据时间约束条件执行。
- 入参’JobParameters’也是JobInfo中设置好的参数,在这里有一个特殊的接口’getStopReason’与静态方法’getReasonName’可以获取调用onStopJob的原因。
- onStopJob调用时,本质是表明需要停止当前的任务,但是并不能立即停止当前的任务,需要在执行耗时的任务中检测onStopJob的标志,获取读取JobParameters 参数。非常类似停止线程中的interrupt,只是起通知作用。
jobFinished(JobParameters params, boolean wantsReschedule)
当JobService 子类执行耗时任务时,主动停止任务
- true 本次任务满足重试要求,会根据重试策略参见
JobInfo
中的setBackoffCriteria
,来重新调度任务。 - false 本次任务执行完成,系统会根据时间约束条件来重新调度任务。
JobInfo 注意事项
JobInfo的主要功能是构造任务的约束条件以及相任务传递参数。
约束条件
约束条件,我个人分为终端状态约束条件,时间相关约束条件两种
- 终端状态约束条件有。网络状态
setRequiredNetworkType
,终端电量状态setRequiresCharging
,终端存储空间状态setRequiresStorageNotLow
,终端是否处于空闲状态setRequiresDeviceIdle
。重启后是否继续有效(本质是将任务序列化存储,并监听开机广播)这些约束条件,传入boolean的值或者固定的值,这些约束条件,只的是在特定情况发生,只要状态不满足就不会发生,也不会重试。 - 时间相关约束条件,周期性任务
setPeriodic
,非周期性任务setMinimumLatency
,setOverrideDeadline
。重试策略setBackoffCriteria
。
- 一个任务不能是周期任务或者非周期任务,因此
setPeriodic
和setMinimumLatency
,setOverrideDeadline
不能同时设置。 - 非周期性任务,通过设置
setMinimumLatency
,setOverrideDeadline
与重试策略setBackoffCriteria
搭配可以保证,任务尽量能够执行成功。代码如下
JobInfo jobInfo = builder
// 进度调度后,最大延迟3秒,会进行调度
.setOverrideDeadline(3*1000)
.setBackoffCriteria(1000,JobInfo.BACKOFF_POLICY_LINEAR)
.build();
// 任务
public SimpleService extends JobService{
public boolean onStartJob(JobParameters params) {
new Thread(new Runnable() {
@Override
public void run() {
boolean flag = true;
// 判断任务状态
if (flag){
// 根据重试策略,再次执行
// 再次执行时间为当前时间+重试次数*1000 (毫秒)(重试时间根据不同的重试策略不同)
jobFinished(params,true);
}else {
// 任务执行完成,不在执行
jobFinished(params,true);
}
}
}).start();
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG,"onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
// 在这里可以根据取消任务的原因来决定是否需要执行重试策略。
return true;
}
}
- 周期性任务跟重试策略也可以进行搭配,但是这个的话,就较为复杂一点,时间的计算更为复杂。
JobInfo jobInfo = builder
// 进度调度后,最大延迟3秒,会进行调度
.setPeriodic(3*1000)
.setBackoffCriteria(1000,JobInfo.BACKOFF_POLICY_LINEAR)
.build();
// 任务
public SimpleService extends JobService{
public boolean onStartJob(JobParameters params) {
new Thread(new Runnable() {
@Override
public void run() {
boolean flag = true;
// 判断任务状态
if (flag){
// 根据重试策略,再次执行
// 再次执行时间为当前时间+重试次数*1000 (毫秒)(重试时间根据不同的重试策略不同)
jobFinished(params,true);
}else {
// 表明当前任务执行完成,周期性执行下一个任务。
/**
周期性执行下一个任务分两种情况:
1. 当 android API 版本在21 ~ 24。为当前时间+周期间隔时间。
2. 当 android API 版本打与24,间隔时间如果任务执行时间不超过flexMillis,则还是以任务结束时间后加间隔时间开始再次调度,如果任务时间超过flexMillis,则当时间到达flexMillis,后再次计时调度。
3. 这就是 setPeriodic 一个传入参数和 两个传入参数的区别与注意事项。
**/
jobFinished(params,true);
}
}
}).start();
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG,"onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
// 在这里可以根据取消任务的原因来决定是否需要执行重试策略。
return true;
}
}
- 时间约束条件在android7.0 以后有了更大的约束,周期性时间必须大于15分钟,重试间隔必须打印10秒。因此在不同的版本需要特别注意。
传递参数
传递参数的接口有
- setExtras 当任务创建时,传给任务的参数。
- setTransientExtras 设置
persisted
,则不能使用这个来传递参数。 - setClipData 这只剪贴板的数据。
数据更改触发任务
这些接口是
- addTriggerContentUri(mUrl) 监听url对应数据变化,触发当前任务执行
- setTriggerContentMaxDelay 数据变化------->任务执行 最大延迟
- setTriggerContentUpdateDelay 更新 延迟
JobId问题
在构建JobInfo时,最为重要的是JobId。源码如下
public Builder(int jobId, @NonNull ComponentName jobService) {
mJobService = jobService;
mJobId = jobId;
}
jobId 必须是整个系统唯一的,不仅仅是当前应用唯一值。这就涉及两个问题,如果查询到整个系统的JobId,是否可以通过接口scheduler.getAllPendingJobs()
查询到,如果可以查询到是否可以通过接口cancel
其他应用创建的任务。这是不是涉及到一个权限问题。
-
问题1 如何保证jobId是全系统唯一的,如果不唯一有什么影响
通过测试发现。如果进程不相同,无法获得其他进程创建的job。这样有一个好处,其他进行也无法取消本进程创建的Job。
- 问题2 如果不同进程创建了相同的JobId,对系统调度有什么影响?
个人觉得,可能不会对Job的调度造成影响,原因是,当前进程都无法知道系统里是否有其他进程创建的JobService,因此无法保障创建JobServeice时的JobId是唯一的。这就让我对源码中的注释产生了疑问,注释中强调需要保证JobId的唯一性,但是从实际情况来说很难保证,那开发时需要注意什么呢?
- 问题2 如果不同进程创建了相同的JobId,对系统调度有什么影响?
JobScheduler 调试问题
在Android7.0 以后,周期性的实际必须大于等于15分钟,这是比较难触发的,在开发过程中,不可能等15分钟调试一次,因此需要使用adb进行调试。目前发现在android7.0 以后的终端都有这个命令,但是命令的执行选项根据版本的不同会有所不同。
命令:adb shell cmd jobscheduler
根据命令对JobScheduler 进行测试。
Job scheduler (jobscheduler) commands:
help
Print this help text.
run [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID
Trigger immediate execution of a specific scheduled job.
Options:
-f or --force: run the job even if technical constraints such as
connectivity are not currently met
-u or --user: specify which user's job is to be run; the default is
the primary or system user
timeout [-u | --user USER_ID] [PACKAGE] [JOB_ID]
Trigger immediate timeout of currently executing jobs, as if their.
execution timeout had expired.
Options:
-u or --user: specify which user's job is to be run; the default is
all users
cancel [-u | --user USER_ID] PACKAGE [JOB_ID]
Cancel a scheduled job. If a job ID is not supplied, all jobs scheduled
by that package will be canceled. USE WITH CAUTION.
Options:
-u or --user: specify which user's job is to be run; the default is
the primary or system user
heartbeat [num]
With no argument, prints the current standby heartbeat. With a positive
argument, advances the standby heartbeat by that number.
monitor-battery [on|off]
Control monitoring of all battery changes. Off by default. Turning
on makes get-battery-seq useful.
get-battery-seq
Return the last battery update sequence number that was received.
get-battery-charging
Return whether the battery is currently considered to be charging.
get-battery-not-low
Return whether the battery is currently considered to not be low.
get-storage-seq
Return the last storage update sequence number that was received.
get-storage-not-low
Return whether storage is currently considered to not be low.
get-job-state [-u | --user USER_ID] PACKAGE JOB_ID
Return the current state of a job, may be any combination of:
pending: currently on the pending list, waiting to be active
active: job is actively running
user-stopped: job can't run because its user is stopped
backing-up: job can't run because app is currently backing up its data
no-component: job can't run because its component is not available
ready: job is ready to run (all constraints satisfied or bypassed)
waiting: if nothing else above is printed, job not ready to run
Options:
-u or --user: specify which user's job is to be run; the default is
the primary or system user
trigger-dock-state [idle|active]
Trigger wireless charging dock state. Active by default.