JobScheduler 运行机制

2024-09-06 06:20
文章标签 jobscheduler 运行机制

本文主要是介绍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的创建到执行

  1. APP->>JobScheduler:JobScheduler()
  2. JobScheduler->>JobScheduler:schedule()
  3. JobSchedulerImpl->>JobSchedulerService:schedule()
  4. JobSchedulerService->>JobSchedulerService:scheduleAsPackage()
  5. JobSchedulerService->>JobStatus:createFromJobInfo()
  6. JobSchedulerService->>JobSchedulerService:startTrackingJobLocked()
  7. JobSchedulerService->>JobSchedulerService:isReadyToBeExecutedLocked()
  8. JobSchedulerService->>JobSchedulerService:addOrderedItem()
  9. JobSchedulerService->>JobConcurrencyManager:assignJobsToContextsLocked()
  10. JobConcurrencyManager->>JobConcurrencyManager:startJobLocked()
  11. JobConcurrencyManager->>JobServiceContext:executeRunnableJob()
  12. JobServiceContext->>JobService:bindServiceAsUser()
  13. JobService->>JobService:onBind()
  14. JobService->>JobServiceEngine:Engine.getBinder()
  15. JobService->>JobServiceContext:onServiceConnected()
  16. JobServiceContext->>JobServiceContext:doServiceBoundLocked()
  17. JobServiceContext->>IJobService:startJob()
  18. IJobService->>JobServiceEngine:startJob()
  19. JobServiceEngine->>JobServiceEngine:onStartJob()
  20. JobServiceEngine->>JobService:onStartJob()
  21. JobService->>App:onStartJob()
  22. 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服务的构造函数的实现:

  1. 获取系统服务(PackgeManager和ActivityManager);通过PM获取uid,AM获取是否异常应用
  2. 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)
  3. Job常量数量和时间的初始化(前后台,idle,充电,网络连接, 电量,存储,Standby, 失效时间) 以及settings数据库与jobscheduler相关的数据变化的监听
  4. JobSchedulerStub的实现,继承IJobScheduler.Stub的接口,用于与app进行通讯(schedule函数)
  5. 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()
  1. 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()
  1. 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()
  1. 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 运行机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141246

相关文章

JobScheduler 调用导致的运行时长30分钟的功耗问题

一、SDK 的使用情况与功耗影响 案例是否导致功耗变大onStartJob return true 且子线程没有调用jobFinished()告知系统功耗变大,最长带来30分钟的partial wakelock 长持锁onStartJob return true 且子线程调用jobFinished()告知系统功耗有影响,主要线程执行时长,标准是30秒内onStartJob return fals

Oracle数据库(Oracle存储结构、Oracle运行机制、日期相关的函数、序列、大对象数据类型、表的修改与约束、事务)

仅仅只是阅读浏览博客内容学习Oracle这种方式不行,更重要的是敲代码,推荐博客中代码建议阅读本博客的人去手动的敲一下代码! 推荐博客: http://blog.csdn.net/ochangwen/article/details/52214713 一、Oracle存储结构 在了解Oracle物理存储结构之前,首先了解一些表空间和数据库的结构组成。表空间是一个数据库被分成若干

spawn-fcgi与fcgi的运行机制分析

转自:http://blog.csdn.net/cleanfield/article/details/6412723 这几天看了spawn-fcgi的源代码,以及libfcgi的源代码,终于明白了c程序fcgi的运行机制,这里画了一个时序图。官方的spawn-fcgi是没有守护监控功能的,我在此基础上实现了守护监控功能,ab并发测试,效果不错,有需要的同学可以直接下载 http://do

JobScheduler 开发自测调试

1. 目标 例如以下模拟数据 相同时间内灭屏待机情况 有Job优化版本 无Job优化版本 数据展示 剩余电量 50 45 续航提升5% 时间延迟次数 100 0 N/A,体现数据优化原因 拦截Job次数 132 0 N/A,体现数据优化原因 第三方App的Job 执行总次数(越大越耗电) 20 200 优化后,减少(1-20/200=90%)90

浅析SSL/TLS协议基本运行机制

前言   在手动搭建kubernetes集群的时候,涉及到了TLS认证的配置,其步骤较为繁琐,如果不清楚TLS背后的流程和原理的话,会遇到各种各样千奇百怪的问题。   本文会简要介绍SSL/TLS协议的运行机制,帮助我们理解kubernetes集群的配置,具体配置步骤会在后文中总结。 概述   TLS(Transport Layer Security,传输层安全协议),其前身为 SSL

初探JAVA代码在虚拟机中的运行机制

前言 从这篇文章开始,后面我们将持续介绍JAVA虚拟机的工作原理。作为一名Android程序员,我们都知道Java代码有很多种运行方式,比如:可以在命令行中运行,可以在开发工具中运行,可以以jar文件的形式运行,甚至可以在网页中运行。这些执行的方式都离不开JRE(Java Runtime Environment) 即Java运行时环境。 我们平时开发之前搭建环境安装的JDK里面也有JRE,下面这

MFC运行机制--笔记

刚接触MFC的时候,一头雾水,一堆代码,一个创建好的窗口,和一些实现的功能,根本不知道从何下手来编写代码。时至今日,其运行机制也跟踪过多次,深入浅出MFC也看过了,但还是不太明白,该在哪个函数中干什么事,好多时候都是copy别人的代码,自己也不甚明了,行为模式越来越类似码农!今日痛下决心,遇见一个问题必定解决之,绝不拖延,每解决一个问题都要写下来,以后想起还可以重新温习。接下来就先剖析和跟踪MFC

Kafka的Broker运行机制

目录 1.Zookeeper整体数据 2.Controller Broker选举机制 3.Leader Partition选举机制 4.Leader Partition自平衡机制 5.Partition故障恢复机制 6.HW一致性保障-Epoch更新机制 总结 Kafka依赖很多的存储数据,但是,总体上是有划分的。Kafka将状态信息保存在Zookeeper中,通过Zook

关于构造函数和原型链运行机制的试题与知识点总结

题目: 如何准确判断一个变量是数组类型写一个原型链继承的例子描述new一个对象的过程zepto(或其他框架)源码中如何使用原型链 知识点: 1. 构造函数 构造函数要用大写字母开头var a=其实是var a=new Object)的语法糖var a=[]其实是var a=new Array)的语法糖function Foo){…}其实是var Foo=new Function(.)使用i

Kafka运行机制(一):Kafka集群启动,controller选举,生产消费流程

前置知识 Kafka基本概念https://blog.csdn.net/dxh9231028/article/details/141270920?spm=1001.2014.3001.5501 1. Kafka集群启动 Kafka在启动集群中的各个broker时,broker会向controller注册自己,并且从controller节点同步集群元数据。 broker是Kafka集群中的