本文主要是介绍JobScheduler 运行机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
学习文档
https://blog.csdn.net/u011311586/article/details/83027820
一.什么是JobScheduler?
。在Android Lollipop版本中增加了JobScheduler API,JobScheduler翻译为任务调度器,可以替代WakeLodk和Alarm运行任务。
JobScheduler是一个系统提供的框架, 是将任务(Job)放到后台,当预制的条件被满足时,这些Job会在后台被执行。
1.1 JobScheduler的省电功能
使用JobScheduler替代WakeLock和Alarm运行任务,是因为后者在每个APP中是相互独立的,而JobScheduler运行在操作系统层面。2014年Google开发大会上指出,如果每个APP都使用这个API,那么可以节约15%到20%的电量。
单独访问1000次,和将1000次放到一起访问;前者更加耗电,因为会重复的激活网络开启状态
JobSchedule可以根据传入的一些参数做些决策(最小延时、最多延时、网络类型)
当满足条件时,再去执行任务
二.框架简述
2.1 Job任务执行原理概览
使用者构建Job任务,把其构建信息传递给Job任务管理系统;任务管理系统负责Job任务满足条件的触发,Job任务相应的执行和生命周期管理。
执行模块如下图所示,紫色区域代表Job任务使用者创建模块,红色区域代表Job任务系统管理模块,蓝色区域代表Job任务状态控制模块,绿色区域代表Job任务生命周期管理模块的实现。
2.2 Job的创建到执行
- APP->>JobScheduler:JobScheduler()
- JobScheduler->>JobScheduler:schedule()
- JobSchedulerImpl->>JobSchedulerService:schedule()
- JobSchedulerService->>JobSchedulerService:scheduleAsPackage()
- JobSchedulerService->>JobStatus:createFromJobInfo()
- JobSchedulerService->>JobSchedulerService:startTrackingJobLocked()
- JobSchedulerService->>JobSchedulerService:isReadyToBeExecutedLocked()
- JobSchedulerService->>JobSchedulerService:addOrderedItem()
- JobSchedulerService->>JobConcurrencyManager:assignJobsToContextsLocked()
- JobConcurrencyManager->>JobConcurrencyManager:startJobLocked()
- JobConcurrencyManager->>JobServiceContext:executeRunnableJob()
- JobServiceContext->>JobService:bindServiceAsUser()
- JobService->>JobService:onBind()
- JobService->>JobServiceEngine:Engine.getBinder()
- JobService->>JobServiceContext:onServiceConnected()
- JobServiceContext->>JobServiceContext:doServiceBoundLocked()
- JobServiceContext->>IJobService:startJob()
- IJobService->>JobServiceEngine:startJob()
- JobServiceEngine->>JobServiceEngine:onStartJob()
- JobServiceEngine->>JobService:onStartJob()
- JobService->>App:onStartJob()
- App->>JobService:jobFinished()
1. JobScheduler:暴露给客户端的接口类,它是一个抽象类,通过getSystemService()获取实例;
2. JobSchedulerImpl:JobScheduler的具体实现类,客户端实际拿到的是该类实例,其中携带一个Binder实例,将调用进入JobSchedulerService;
3. JobInfo:暴露给客户端的用于创建Job的类,封装了创建Job时所设置的所有信息;
4. JobService:继承于Service,客户端需要JobService的子类来执行任务;
5. JobSchedulerService:继承于SystemService的系统服务,负责调度所有的Job;
6. JobStatus:Framework层内部表示Job的唯一标识,通过JobInfo创建,其中存储了所有的Job信息;
7. JobStore:负责维护JobSchedulerService正在跟踪的Job的列表,还支持一些遍历Job、读写文件等操作(将job信息写入/data/system/job/jobs.xml中)。在获取JobStore实例时,通过单例模式保证JSS只持有一个该类对象;
8. StateController:状态控制器的基类,每个子类控制器单独负责控制一个系统状态,当状态发生变化后,将通知JobSchedulerService进行处理;
9. StateChangedListener:状态控制器的回调接口,所有控制器通过该接口来通知JobSchedulerService;
10. JobServiceContext:继承于ServiceConnection,负责和应用的JobService绑定和作业生命周期管理。此类的一个实例上只能执行一个作业。
11. JobServiceEngine:应用端的JobService和JobSchedulerService进行交互的"引擎",帮读一个客户端Service后,将由该类去调用客户端的onStartJob()方法开始执行任务;
12. JobCompletedListener:作业执行完毕后的回调接口。
针对整个框架的实现原理,可以简单地这样描述: 首先通过JobScheduler创建一个Job,接着JobSchedulerService将负责调度job,经过各种StateController对其进行跟踪,当满足条件后,将通过JobServiceContext和客户端JobService进行绑定并对Job进行生命周期管理,然后在生命周期的某个阶段中通过JobServiceEngine调用客户端方法来执行Job任务,执行完成后,又在生命周期的某个阶段回调JobCompletedListener接口通知JobSchedulerService。
1. JobScheduler->>JobScheduler:schedule()
客户端的调用demo
JobScheduler:暴露给客户端的接口类,它是一个抽象类,通过getSystemService()获取实例;
其中有两个关键类,JobInfo.Builder, JobScheduler,我们详细分析一下这两个接口类能提供给应用哪些功能。
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);if (null != jobScheduler) {jobScheduler.cancel(SCREEN_OFF_JOB_ID);int delay = SCREEN_OFF_INSPECT_DELAY;int maxDelay = SCREEN_OFF_INSPECT_DELAY + DELAY_WINDOW;ComponentName jobService = new ComponentName(context, ScreenOffJobService.class);// JobInfo.Builder 利用了建造模式,内部类的运用// 3. JobInfo:暴露给客户端的用于创建Job的类,封装了创建Job时所设置的所有信息;JobInfo.Builder builder = new JobInfo.Builder(SCREEN_OFF_JOB_ID, jobService);builder.setMinimumLatency(delay);builder.setOverrideDeadline(maxDelay);PersistableBundle args = new PersistableBundle();builder.setExtras(args);jobScheduler.schedule(builder.build());Log.d(TAG, "startScreenOffJob");}原生定义的优先级常量, 这个位置我们可以定制一些系统级应用的优先级
PRIORITY_DEFAULT
PRIORITY_SYNC_EXPEDITED
PRIORITY_SYNC_INITIALIZATION
PRIORITY_BOUND_FOREGROUND_SERVICE
PRIORITY_FOREGROUND_APP
PRIORITY_FOREGROUND_SERVICE
PRIORITY_FOREGROUND_SERVICE
PRIORITY_TOP_APP
PRIORITY_ADJ_OFTEN_RUNNING
PRIORITY_ADJ_ALWAYS_RUNNING所以这个优先级设置的接口是给系统应用和系统服务开放的,可以在创建任务时候主动设置,其他三方应用,或没有设置priority的应用,JobSchedulerService会在uid发生变化时自动更新每个uid的优先级。@hide setFlags(int flags)
FLAG_WILL_BE_FOREGROUND 设置后,此作业的内部调度将忽略请求应用程序的任何后台网络限制。请注意,仅此标志实际上并没有将您的 {@link JobService} 放在前台;您仍然需要自己发布通知
FLAG_IMPORTANT_WHILE_FOREGROUND 只要应用程序在前台或临时白名单中,就允许此作业在打瞌睡限制的情况下运行
FLAG_PREFETCH 预加载
FLAG_EXEMPT_FROM_APP_STANDBY 该作业需要免除应用待机节流。只有系统(UID 1000)可以设置它。有时间限制的工作不能有它。
FLAG_EXPEDITED 不管是不是加急工作。setExtras(@NonNull PersistableBundle extras)
setTransientExtras(@NonNull Bundle extras)
setClipData(@Nullable ClipData clip, int grantFlags)setRequiredNetworkType(@NetworkType int networkType)
设置启动job需要有网络设置有网络才能执行jobsetEstimatedNetworkBytes(@BytesLong long downloadBytes, @BytesLong long uploadBytes)
设置此作业将执行的网络流量的估计大小,以字节为单位。
setRequiresCharging(boolean requiresCharging)
指定要运行此作业,设备必须正在充电
setRequiresBatteryNotLow(boolean batteryNotLow)
指定要运行此作业,设备的电池电量不得过低。
setRequiresDeviceIdle(boolean requiresDeviceIdle)
设置 {@code true} 时,确保在设备处于活动使用状态时不会运行此作业
setRequiresStorageNotLow(boolean storageNotLow)
指定要运行此作业,设备的可用存储空间不得过低。
addTriggerContentUri(@NonNull TriggerContentUri uri)
添加一个新的内容:将使用 {@link android.database.ContentObserver} 监控的 URI,如果更改将导致作业执行。如果您有任何与作业关联的触发器内容 URI,则在其中一个或多个更改报告出现之前,它不会执行。
setTriggerContentUpdateDelay(long durationMs)
设置从检测到内容更改到计划作业的延迟(以毫秒为单位)。如果在此期间有更多更改,则延迟将重置为在最近更改时开始。
setTriggerContentMaxDelay(long durationMs)
设置从第一次检测到内容更改到计划作业为止允许的最大总延迟(以毫秒为单位)。
setPeriodic(long intervalMillis)
指定此作业应以提供的间隔重复,每个周期不超过一次。您无法控制在此时间间隔内何时执行此作业,只能保证在此时间间隔内最多执行一次。
setMinimumLatency(long minLatencyMillis)
指定此作业应延迟提供的时间量。延迟过后,作业可能不会立即运行。 JobScheduler 将在延迟过去后的不确定时间启动作业。
setOverrideDeadline(long maxExecutionDelayMillis)
设置最后期限,即最大调度延迟。
setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)
设置后退重试策略。这默认为一些可观的值:{30 秒,指数}。我们将回退限制在 5 小时。
setExpedited(boolean expedited)
设置重要性,类似于加急任务
setImportantWhileForeground(boolean importantWhileForeground)
将此设置为 true 表示当调度应用程序位于前台或在后台限制的临时白名单上时,此作业很重要。这意味着系统将在此期间放松对该作业的打盹限制。
setPrefetch(boolean prefetch)
将此设置为 true 表示此作业旨在预取内容
setPersisted(boolean isPersisted)
设置是否在设备重新启动后保留此作业JobInfo
JobInfo是个modoule 类,除了上面的set方法,剩下到就是一些对应的get方法,以及属性信息, 这里就不过多阐述了。
2. JobSchedulerImpl->>JobSchedulerService:schedule()
Binder 通信传递到 JobSchedulerService
JSS是Job任务的核心模块,系统管理服务是Job任务功能的核心;对使用者起着通讯及同步的作用,担负着任务调用,任务执行状态开启停止及完成;主要管理功能有如下:
- 控制Job的运行数量JobConcurrencyManager(总体,后台最小,后台最大,根据内存压力配置数量组(ProcessStats.ADJ_MEM_FACTOR_*))。
1. public static final int ADJ_NOTHING = -1;
2. public static final int ADJ_MEM_FACTOR_NORMAL = 0;
3. public static final int ADJ_MEM_FACTOR_MODERATE = 1;
4. public static final int ADJ_MEM_FACTOR_LOW = 2;
5. public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
6. public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; - 接收调用者创建的Job,将数据存入到JobStore。
- 初始化Job的状态控制模块,将各个Job相应的触发条件分别更新到各个状态控制模块。
- 常量配置,可执行Job数量,权重因子等。
- 创建平台层Job任务为JobStatus(JobInfo为其构造函数的首参数),平台层以此为来处理和维护Job。
- Job任务的消息处理,执行,过期,停止;UID状态更新(包含change, gone, active, idle)。
主要再介绍JobSchedulerService服务的构造函数的实现:
- 获取系统服务(PackgeManager和ActivityManager);通过PM获取uid,AM获取是否异常应用
- Jobhandler处理;负责管理服务内部的消息处理;主要分两类,Job任务状态的消息处理(MSG_JOB_EXPIRED, MSG_CHECK_JOB,MSG_STOP_JOB),Job任务Active根据UID状态更新(MSG_UID_ACTIVE更新Job任务Active为ture ,MSG_UID_IDLE和MSG_UID_GONE更新Job任务Active为false)
- Job常量数量和时间的初始化(前后台,idle,充电,网络连接, 电量,存储,Standby, 失效时间) 以及settings数据库与jobscheduler相关的数据变化的监听
- JobSchedulerStub的实现,继承IJobScheduler.Stub的接口,用于与app进行通讯(schedule函数)
- JobConcurrencyManager的初始化, 该类是根据配置和系统状态允许多少个Job任务共同运行
// IJobScheduler implementation@Overridepublic int schedule(JobInfo job) throws RemoteException {...return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,null);...}
3. JobSchedulerService->>JobSchedulerService:scheduleAsPackage()
- JobSchedulerService:继承于SystemService的系统服务,负责调度所有的Job;
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,...// 创建 JobStatusJobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);...startTrackingJobLocked(jobStatus, null);...if (isReadyToBeExecutedLocked(jobStatus)) {// This is a new job, we can just immediately put it on the pending// list and try to run it.mJobPackageTracker.notePending(jobStatus);addOrderedItem(mPendingJobs, jobStatus, mPendingJobComparator);maybeRunPendingJobsLocked();} else {evaluateControllerStatesLocked(jobStatus);}...}
4. JobSchedulerService->>JobStatus:createFromJobInfo()
- JobStatus:Framework层内部表示Job的唯一标识,通过JobInfo创建,其中存储了所有的Job信息;
public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg,int sourceUserId, String tag) {return new JobStatus(job, callingUid, sourcePkg, sourceUserId,standbyBucket, tag, 0,earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,/*innerFlags=*/ 0, /* dynamicConstraints */ 0);}
5. JobSchedulerService->>JobSchedulerService:startTrackingJobLocked()
- StateController:状态控制器的基类,每个子类控制器单独负责控制一个系统状态,当状态发生变化后,将通知JobSchedulerService进行处理;
/** List of controllers that will notify this service of updates to jobs. */final List<StateController> mControllers;private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {...for (int i = 0; i < mControllers.size(); i++) {StateController controller = mControllers.get(i);if (update) {controller.maybeStopTrackingJobLocked(jobStatus, null, true);}controller.maybeStartTrackingJobLocked(jobStatus, lastJob);}...}
5. JobSchedulerService->>StateController:maybeStartTrackingJobLocked()
public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
6. JobSchedulerService->>JobSchedulerService:isReadyToBeExecutedLocked()
/*** 如果一个job 满足一定条件需要立即执行,那么会将其放在pending 列表中,并且在后面马上处理** Criteria for moving a job into the pending queue:* - It's ready.* - It's not pending.* - It's not already running on a JSC.* - The user that requested the job is running.* - The job's standby bucket has come due to be runnable.* - The component is enabled and runnable.* 将Job移入待处理队列的标准:* - 已准备就绪* - 非pending* - 没有在 JSC 上运行* - Job正在运行* - 作业的备用存储桶由于可运行而到来(可见进程、备份)* - 组件已启用且可运行*/@VisibleForTesting@GuardedBy("mLock")boolean isReadyToBeExecutedLocked(JobStatus job) {return isReadyToBeExecutedLocked(job, true);}
7. JobSchedulerService->>JobSchedulerService:addOrderedItem()
mPendingJobComparator);//5.添加到"将要运行job"队列中,将jobStatus 添加到 mPendingJobs
if (isReadyToBeExecutedLocked(jobStatus)) {// This is a new job, we can just immediately put it on the pending// list and try to run it.// 如果一个job 满足一定条件需要立即执行,那么会将其放在pending 列表中,并且在后面马上处理mJobPackageTracker.notePending(jobStatus);// 根据包名检测运行时间量addOrderedItem(mPendingJobs, jobStatus, mPendingJobComparator);//5.添加到"将要运行job"队列中,将jobStatus 添加到 mPendingJobsmaybeRunPendingJobsLocked();} else {// 如果Job还没有准备好运行,就没有什么可做的了,——我们现在只是等待它的一个控制器改变状态并适当地安排作业。evaluateControllerStatesLocked(jobStatus);}static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {int where = Collections.binarySearch(array, newItem, comparator);if (where < 0) {where = ~where;}array.add(where, newItem);}
7. JobSchedulerService->>JobSchedulerService:maybeRunPendingJobsLocked()
/*** Reconcile(调和) jobs in the pending queue against available execution contexts.* A controller can force a job into the pending queue even if it's already running, but* here is where we decide whether to actually execute it.** 根据可用的执行上下文协调待处理队列中的job。* controller可以强制一个Job进入待处理队列,即使它已经在运行,但我们决定是否需要这样做。*/void maybeRunPendingJobsLocked() {if (DEBUG) {Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");}//
这篇关于JobScheduler 运行机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!