Android 7.0之JobScheduler 分析——如何使用job

Posted 风雨田

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 7.0之JobScheduler 分析——如何使用job相关的知识,希望对你有一定的参考价值。

背景

最近公司好多人都因为Jobscheduler的使用不当导致各种问题,Job定时任务不生效或者冲突。归根结底是对Jobscheduler的使用不熟悉以及,其工作原理没有一个系统性的了解。本人也曾踩坑,所以下定决心好好熟悉Jobscheduler。

概述

   在android开发中经常会有这样的需求,开发者需要在稍后的某个时间点或者满足某个特定的条件时去执行某个任务,例如当设备开始充电,或者网络状态连接到wifi状态时执行某些推送通知的任务,jobscheduler就是用来处理这类场景的任务。
  Jobscheduler的android在5.0上针对于降低功耗而提出来的一种策略方案,自 Android 5.0 发布以来,JobScheduler 已成为执行后台工作的首选方式,其工作方式有利于用户。应用可以在安排作业的同时允许系统基于设备状态、电源和连接情况等具体条件进行优化。JobScheduler 可实现控制和简洁性,谷歌推出该机制是想要所有应用在执行后台任务时使用它。(还有一点需要注意的是,在7.0上谷歌给出建议:在 Android 7.0 中,删除了三个常用隐式广播 —CONNECTIVITY_ACTION、ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO— 因为这些广播可能会一次唤醒多个应用的后台进程,同时会耗尽内存和电量。如果应用需要收到这些广播,充分利用 Android 7.0 以迁移到 JobScheduler 和相关的 API。)

如何使用jobscheduler

构建属于你的job任务

应用如果想使用JobScheduler API的话,首先需要创建自己需要执行的任务信息,创建任务的方法在谷歌官方文档上已经有详细介绍,这里只是放出一个实例:


JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
 ComponentName jobService = new ComponentName(this, MyJobService.class);

 JobInfo jobInfo = new JobInfo.Builder(100012, jobService) //任务Id等于100012
         .setMinimumLatency(5000)// 任务最少延迟时间为5s  
         .setOverrideDeadline(60000)// 任务deadline,当到期没达到指定条件也会开始执行  
         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 需要满足网络条件,默认值NETWORK_TYPE_NONE
         .setPeriodic(AlarmManager.INTERVAL_DAY) //循环执行,循环时长为一天(最小为15分钟)
         .setRequiresCharging(true)// 需要满足充电状态  
         .setRequiresDeviceIdle(false)// 设备处于Idle(Doze)
         .setPersisted(true) //设备重启后是否继续执行
         .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) //设置退避/重试策略
         .build();  
 scheduler.schedule(jobInfo);



上面的一个任务需要满足充电状态,并且设备不出于idle状态,并且需要网络处于非计费类型时,会运行该job,自己在构建自己的job 时候需要按需设置触发条件。但是这里需要注意的一个点是JobScheduler所创建并执行的任务必须是带有条件限制的,不然是违背其初衷的,当你创建一个任务,并且不设置任何限制条件并且直接调用 scheduler.schedule(builder.build());去执行该任务是不可行的,会报以下的异常

    java.lang.IllegalArgumentException: You're trying to build a job with no constraints, this is not allowed.



1. setMinimumLatency(long minLatencyMillis): 设置任务的最小延迟执行时间(单位是毫秒)。
2. setOverrideDeadline(long maxExecutionDelayMillis): 设置任务最晚的延迟时间。如果到了规定的时间时其他条件还未满足,你的任务也会被启动。
3. setPersisted(boolean isPersisted): 设置当设备重启之后该任务是否还要继续执行。
4. setExtras(PersistableBundle extras): 设置传递bundler参数
5. setRequiredNetworkType(int networkType):设置需要满足网络类型
6. setRequiresCharging(boolean requiresCharging):设置是否需要充电状态下运行
7. setRequiresDeviceIdle(boolean requiresDeviceIdle):设置是否需要在Idle(Doze)状态下运行(7.0上新加的)
8. addTriggerContentUri(@NonNull TriggerContentUri uri):设置监控ContentUri 发生改变时运行(7.0上新加的)
9. setPeriodic(long intervalMillis):设置循环执行的时长。

等等接口都有官方解释,需要设置什么条件,使用对应设置即可。


Job任务要干啥呢?

当需要使用JobScheduler 来干实际的任务时,需要新建一个service,来继承JobService(这一点与DreamService类似),而且必须重写其中的两个方法,分别是onStartJob(JobParameters params)和onStopJob(JobParameters params);
在startJob里来执行自己的代码逻辑

public class MyJobService extends JobService 
    @Override
    public boolean onStartJob(JobParameters params) 
        Log.i("MyTest", "onStartJob @@@@@@@@ " + params);
        jobFinished(params,true);
        return false;
    

    @Override
    public boolean onStopJob(JobParameters params) 
        return false;
    



这里需要注意的一点是,当做完自己的任务要及时调用JobFinished 来结束自己的任务。在6.0上在如果不调用该接口会造成严重的功耗问题。

当上面创建任务时执行到scheduler.schedule(builder.build()); 则开始准备执行任务,一旦满足设置的条件,便会执行到onStartJob()方法,也就是在我们的任务应该具体事宜应该是放在onStartJob中去做的。该方法返回值为为一个boolean值,如果返回值是false,系统假设这个方法返回时任务已经执行完毕,如果返回值是true,那么系统假定这个任务正要被执行,执行任务的重担就落在了你的肩上(这时候需要去新开一个线程去做事物,文档接口上有起描述)。当任务执行完毕后要调用jobFinished()来通知系统。

当系统受到一个cancel请求时会取消该任务(当该任务未执行将其在pending list删除,如果该任务正在执行则停止其任务)。
使用Jobscheduler还需要到AndroidManifest.xml中添加一个service节点让你的应用拥有绑定和使用这个JobService的权限。

<service android:name="com.example.apuser.jobtest.MyJobService"
    android:permission="android.permission.BIND_JOB_SERVICE" />



注意了,注意了,如果是在系统主线程中执行的scheduler.schedule(builder.build()); ,那么你的MyJobService也是运行在你的主线程中的,此时需要考虑重点考虑ANR的问题了,如果执行网络操作,或者文件IO 可能会直接被Android的StrickMode给检测出来,抛异常出来 ,这时候就需要新起一个线程来做了。

号外,注意啦

本篇关于JobScheduler的分析开篇,主要介绍使用JobScheduler 的基本用法,以及对齐相关接口做了一些解释。由于在工作中使用了Job 的场景较多,并且解决了几例相关的bug ,对其中的一些需要注意的地方也有所总结:

1.小心JobService运行在主线程:上面已经说明了,当schedule是在主线程调用的,那么jobsevice则运行则主线程,需要小心主线程ANR 以及严苛模式抛出异常
2.小心cancelAll(): 该方法的功能是取消该uid下的所有jobs,也就是说当存在多个app通过shareUid的方式,那么在其中任意一个app执行cancalAll(),则会把所有同一uid下的app中的jobs都cancel掉。
3.Jobscheduler如果设置了setPersisted(true),则重启后还会再运行,不能使用HashCode 来做JobId
4.job执行完了,一定要记得要调用JobFinished
5.同一个包名下,不能有两个相同的jobId ,如果一个app非常大,两个功能模块为两个程序员维护,则很容易产生沟通不足的情况下,使用了两个相同的JobId,此时只能有一个job生效。另一个无效。该问题可以通过终端命令行: adb shell dumpsys jobscheduler 来查看单个app 的job信息确认


总结

JobScheduler 虽然是在5.0上新增加的一个新服务,但是从L到M,N以及最新的O 上,谷歌Android也是在重点推荐使用该功能,并且在Android O 上谷歌还推出了一套Android vitals 计划,旨在提高Android 系统的功耗,性能,以及稳定性等相关指标,在对功耗上提出来的建议便是,非精确性的定时任务建议使用Job来代替Alarm,能更加准确的满足条件的执行你想要执行的任务。在Android O上JobScheduler更加完善了其条件控制,加上了低存储,低电量策略下的job运行限制,这里将在后面job服务解析中继续提到

以上是关于Android 7.0之JobScheduler 分析——如何使用job的主要内容,如果未能解决你的问题,请参考以下文章

Android源码面试宝典之JobScheduler从使用到原理分析JSS的启动

Android源码面试宝典之JobScheduler从使用到原理分析JobServiceJobInfo

安卓电量优化之JobScheduler使用介绍

Android JobScheduler onStartJob 多次调用

Spark版本定制七:Spark Streaming源码解读之JobScheduler内幕实现和深度思考

(版本定制)第7课:Spark Streaming源码解读之JobScheduler内幕实现和深度思考