电量优化—后台调度之JobScheduler

JobScheduler(基于API Level 28)

Android后台调度机制有多种选择,其中对于在指定特定场景下而触发的任务,那么JobScheduler便是较佳选择;JobScheduler主要用于在未来某个时间、对满足一定条件时触发某项任务。Google官方建议网路请求相关业务放到JobScheduler(由于其批量处理任务可以帮助省电)。

后台任务的总体指导思想是减少、延迟和合并《电量优化的方向》,(1)对于延迟时间执行,通常考虑利用系统的闹钟管理器AlarmManager进行定时管理。(2)对于是否联网、是否充电、是否空闲,一般要监听系统的相应广播,常见的系统广播有:

※ 网络状态变化需要监听:android.net.conn.CONNECTIVITY_CHANGE;

※ 设备是否充电需要监听:Intent.ACTION_POWER_CONNECTED;

※ 设备是否空闲需要监听:Intent.ACTION_SCREEN_OFF;

Android 5.0开始,增加支持一种特殊的机制:JobScheduler任务调度,该工具集成了常见的几种运行条件,使开发者仅需要少量配置,即可完成多项条件配合工作。

特征

1、JobScheduler是Android 5.0(API Level 21)增加的特殊任务调度机制。

2、JobScheduler是将多个任务打包在一个场景下执行(由系统来维护调度时机)。

3、在系统重启后,任务会依然保留在JobScheduler当中,因此不要监听系统启动状态重复设定(这有别于AlarmManager)。

4、如果在一定期限内(默认最小间隔时间15分钟)还没有满足特定执行所需情况,JobScheduler会将这些任务加入队列,并且随后(默认最小5分钟)会进行执行。

API介绍

1、JobScheduler:任务调度器,其核心功能实现在JobSchedulerService中。

2、JobInfo:任务概要信息,通过Builder方式创建。

3、JobService:任务服务。包含两个核心实现方法:onStartJob()、onStopJob()。

1、JobScheduler的创建

翻开源码JobScheduler是个抽象类,它的具体实现类实际是JobSchedulerImpl,而且我们需要通过Context的getSystemService()进行获取:

电量优化—后台调度之JobScheduler_第1张图片
通过Context.getSystemService()方式获取

通过Context.getSystemService获取JobScheduler对象,实际会调用到ContextImpl中:

SystemServiceRegistry从名字其实也可以看出它的作用,在该类的静态代码块中注册了大量相关服务,

JOB_SCHEDULER_SERVICE的注册过程:

电量优化—后台调度之JobScheduler_第2张图片

继续跟踪SystemServiceRegistry.getSystemService()调用流程:

SYSTEM_SERVICE_FETCHERS是一个Map对象,从registerService()方法可以看出,在该Map获取到的是StaticServiceFetcher,通过getService()会调用到createService()(只有第一次会调用,后续会缓存在该实例中),这里实际返回的是JobSchedulerImpl。故通过Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)实际得到是JobSchedulerImpl实例。

JobSchedulerImpl:

电量优化—后台调度之JobScheduler_第3张图片

注意:实际JobScheduler的核心实现都在Framework的JobSchedulerService中,不知道大家有没有注意到JobSchedulerImpl的构造方法传入Binder对象(IJobScheduler.Stub.asInterface(b))。关于JobSchedulerService的实现会在后续文章中介绍。JobScheduleImpl中有关操作最终都会交由该Binder完成跨进程通信。JobScheduleImpl本身并没有做什么事情。

JobScheduler(JobSchedulerImpl)所有Public methods

电量优化—后台调度之JobScheduler_第4张图片

※ cancel()方法

取消指定的作业,如果作业当前正在执行,则会立即停止该作业,并JobService#onStopJob(JobParameters)忽略其方法的返回值。

参数:jobId

要取消作业的唯一标识符,提供给android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)。

※ cancleAll()方法

取消调用程序已调度的所有作业。

※ enqueue()方法

与schedule类似,但允许你为新作业或现有作业排队。如果已经安排了具有相同的ID的作业,它将被替换为新作业JobInfo,但任何先前排队的作业将保留并在下次运行时发送。如果具有相同ID的作业已经在运行,则新作业将为其排队。

参数1:job

加入工作队列的JobInfo,该值不能为null。

参数2:work

新的作业加入队列,将在稍后运行该作业时可用,该值不能为null。

※ getAllPendingjobs()方法

检索调用应用程序已调度的所有作业。返回所有的预订任务的列表。这包括当前启动的作业以及仍在等待运行的作业。该值不会返回null。

※ getPendingJob()方法

查找指定的作业JobInfo。

参数:jobId

根据jobId查找指定的作业,如果找不到指定的作业将返回null。

※ schedule()

安排要执行的作业,将用JobInfo中的新信息替换当前调度的具有相同ID的作业。如果具有给定ID的作业当前正在运行,那么它将被停止。

2、JobService

JobService必须在manifest文件中声明BIND_JOB_SERVICE权限:

              android:permission="android.permission.BIND_JOB_SERVICE" >

电量优化—后台调度之JobScheduler_第5张图片

JobService是一个抽象类,继承自Service,增加了两个抽象方法需要我们实现:

(1)onStartJob(JobParameters params)

(2)onStopJob(JobParameters params)

JobService是如何完成onStartJob()和onStopJob()的调度机制的?

电量优化—后台调度之JobScheduler_第6张图片

在JobService的onBind方法中可以看到:通过匿名内部类方式创建JobServiceEngine,并且分别回调了JobService的onStartJob() 和 onStopJob()方法,其实从这里我们也可以看出,JobService的创建是通过bind的方式。

JobServiceEngine的构造方法:

(1)JobInterface:

首先创建JobInterface对象,看下JobInterface的实现:

电量优化—后台调度之JobScheduler_第7张图片

JobInterface继承自IJobService.Stub,它实际是AIDL的Stub部分,既服务端部分。当满足特定的(JobInfo)执行场景,系统就会触发回调该Stub的startJob() 或 stopJob()。

(2)JobHandler:

电量优化—后台调度之JobScheduler_第8张图片

在JobInterface的startJob() 与 stopJob()会通过该Handler进行分发:(该Handler消息保证在主线程中执行,service.getMainLooper())

电量优化—后台调度之JobScheduler_第9张图片

此时就会回调给我们上面所说的在JobService的onBind()方法中创建JobServiceEngine实例的onStartJob()方法,然后调用JobService的onStartJob()方法。

(3)JobService的onStartJob() 与 onStopJob()返回值作用

电量优化—后台调度之JobScheduler_第10张图片

onStartJob方法返回一个boolean值。假设返回值是false,系统就认为任务已经运行完成,后面的onStopJob方法将不再执行。假设返回值是true,那么系统假定这个任务正要被运行,此时可能就需要开启新的线程来异步执行任务,当任务运行完成时你须要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统,此时调用JobScheduler的cancel或者cancelAll方法则onStopJob会执行。

onStopJob当你主动通知任务执行完毕(jobFinished)之前,系统可能会要求你停止任务,这时将会调用onStopJob方法 。当该任务的需求不再满足时或者通过cancel、cancelAll取消任务时将发生这种状况,系统可能会将你的wakelock释放。返回true表示你希望对该任务重新进行调度,同样需要遵守退避策略;返回false表示你希望放弃该任务。

jobFinished调用此函数通知JobScheduler作业已经完成其工作。当系统接收到该消息时,它将释放为该作业保存的wakelock。

wantsRescheduler参数是告诉系统此任务是否需要进行重新调度,这取决于任务本身需求。

对jobFinished的一些理解:

onStartJob()返回false的话,onStartJob()一经执行完毕,service就被destroy了,这时候不管你调用cancel还是jobFinished都是没有用的(不会回调onStopJob)。onStartJob()返回true的话,onStartJob()一经执行完毕,系统还是觉得JobService还在运行,并给了它10min的执行超时。10min之内自行调用jobFinished的话,系统会直接destroy该Job,而不回调onStopJob()。10min到了前自行调用cancel的话,系统会回调onStopJob(),让你可以处理后台逻辑(例如停止线程)。如果10min到了,你既没有调用jobFinished去通知系统完成了,也没有调用cancel取消Job。那么系统会强制停止你的Job,会回调onStopJob()和destroy你的Job。 还有一点: 为什么取消任务一定要确保onStopJob()被回调呢? 取消任务的结果和Job运行的状态和调用cancel的时机有关。 比如你的Job条件还没达到的时候你去cancel,这时候确保当你的Job条件达到后,这时候Job的onCreate()没有回调就可以了。 比如你的Job条件达到正在运行(onStartJob正在执行耗时逻辑并返回true)的时候你去cancel,这时候Job的onStopJob和onDestroy有回调就可以了。 比如你的Job条件达到已经运行完毕(onStartJob执行完简单逻辑并返回false)的时候你去cancel,这时候Job的onStopJob和onDestroy都不会有回调。

3、JobInfo

JobInfo是用来描述任务的执行条件、策略等,通过Builder模式来创建该实例。它本身继承自Parcelable(熟悉Android的朋友都知道,它是用来解决Android中大量跨进程调度的数据序列化性能问题)。

JobScheculer封装了针对调用应用程序调度工作所需的参数。这些都是使用JobInfo.Builder构造的。必须在正在创建的JobInfo对象上指定至少一种约束。

电量优化—后台调度之JobScheduler_第11张图片

(1)示例说明

电量优化—后台调度之JobScheduler_第12张图片

※ PersitableBundle保存要处理的数据,在未来某个时刻该数据将会被传递到JobService的onStartJob(JobParameters)中:可以通过JobParameters.getExtras()获取到。

※ CoponentName指定要调度的服务。

※ 在该示例中指定充电与WIFI网络环境下触发。

(2)JobInfo的合并。

每个JobInfo都要一个jobId,在每次添加一个新的JobSchedule任务时,我们可以通过判断当前是否已经存在该JobInfo任务:

电量优化—后台调度之JobScheduler_第13张图片

拿到已经存在的JobInfo任务,此时可以合并为一个JobInfo任务:

电量优化—后台调度之JobScheduler_第14张图片

新的location为合并后的数据,此时我们构建该location的新JobInfo数据,从而完成对多个JobInfo任务的合并处理。

示例

Google官方Sample

深入理解JobScheduler与JobService的使用

由于本人能力有限,文章难免会有错误或理解不到位的地方,欢迎您的指正。

你可能感兴趣的:(电量优化—后台调度之JobScheduler)