Android JobScheduler 的使用

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 注意事项

  1. JobScheduler 在不同的版本,其接口是不一样的,因此在使用时,需要特别注意版本兼容问题。
  2. 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 是在后台任务开始时调用这个方法。

  1. 返回值为true,表明onStartJob,执行到return语句时,该任务已执行完成,通知系统进行下一次的调度。由于’onStartJob’在UI线程,因此该任务不能是耗时任务。
  2. 返回值为false,表明’onStartJob’,执行到return语句时,该任务未执行完成,一般这时候是耗时任务,通过线程中执行任务,此时,任务执行完成,需要调用接口jobFinished(),表明任务执行完成,通知系统进行下一次的调度。
  3. onStartJob入参JobParameters,在构建JobInfo时会把参数通过Parameters传递给onStartJob,比如jobId,序列化参数PersistableBundle, 过程参数Bundle等等。

onStopJob 系统决定停止当前正在执行的后台任务时调用。

  1. 调用onStopJob的时机,有两种情况:情况一,任务正在执行,且主动调用JobScheduler的cancel接口。情况二,系统主动停止调度。代码中注释中说如果不满足约束条件(网络条件,电量条件等)会触发,但是经过测试,网络条件改变并不会触发次接口,后面有机会可以进行多项测试确定次问题。
  2. 返回值为true,表明此项任务会通过重试策略再次执行。返回值为false,表明任务执行完成,会根据时间约束条件执行。
  3. 入参’JobParameters’也是JobInfo中设置好的参数,在这里有一个特殊的接口’getStopReason’与静态方法’getReasonName’可以获取调用onStopJob的原因。
  4. onStopJob调用时,本质是表明需要停止当前的任务,但是并不能立即停止当前的任务,需要在执行耗时的任务中检测onStopJob的标志,获取读取JobParameters 参数。非常类似停止线程中的interrupt,只是起通知作用。

jobFinished(JobParameters params, boolean wantsReschedule) 当JobService 子类执行耗时任务时,主动停止任务

  1. true 本次任务满足重试要求,会根据重试策略参见 JobInfo中的setBackoffCriteria,来重新调度任务。
  2. false 本次任务执行完成,系统会根据时间约束条件来重新调度任务。

JobInfo 注意事项

JobInfo的主要功能是构造任务的约束条件以及相任务传递参数。

约束条件

约束条件,我个人分为终端状态约束条件,时间相关约束条件两种

  1. 终端状态约束条件有。网络状态setRequiredNetworkType,终端电量状态setRequiresCharging,终端存储空间状态setRequiresStorageNotLow,终端是否处于空闲状态setRequiresDeviceIdle。重启后是否继续有效(本质是将任务序列化存储,并监听开机广播)这些约束条件,传入boolean的值或者固定的值,这些约束条件,只的是在特定情况发生,只要状态不满足就不会发生,也不会重试。
  2. 时间相关约束条件,周期性任务setPeriodic,非周期性任务setMinimumLatency,setOverrideDeadline。重试策略setBackoffCriteria
  • 一个任务不能是周期任务或者非周期任务,因此setPeriodicsetMinimumLatency,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秒。因此在不同的版本需要特别注意。

传递参数

传递参数的接口有

  1. setExtras 当任务创建时,传给任务的参数。
  2. setTransientExtras 设置persisted,则不能使用这个来传递参数。
  3. setClipData 这只剪贴板的数据。

数据更改触发任务

这些接口是

  1. addTriggerContentUri(mUrl) 监听url对应数据变化,触发当前任务执行
  2. setTriggerContentMaxDelay 数据变化------->任务执行 最大延迟
  3. 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的唯一性,但是从实际情况来说很难保证,那开发时需要注意什么呢?

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.
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值