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需要有网络设置有网络才能执行job

setEstimatedNetworkBytes(@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
    @Override
    public 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,
        ...
        // 创建 JobStatus
        JobStatus 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 添加到 mPendingJobs
        maybeRunPendingJobsLocked();
    } 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是Android中的一个调度框架,它可以用来在特定的时间间隔或特定的条件下执行任务,同时也可以用来保持应用的活跃状态。 在Android系统中,应用程序在后台运行时有一定概率被系统回收释放内存,为了提高应用的用户体验和功能完整性,我们可以使用JobScheduler来保活应用。 具体实现方法如下: 1. 在AndroidManifest.xml文件中注册JobService,用于执行任务和保活应用。设置jobService的权限,以及设置该service的处理逻辑。 2. 在需要保活的地方调用JobScheduler的schedule()方法,创建一个JobInfo对象,并设置好相关参数。其中包括指定要执行的JobService、执行的触发条件(如设备空闲时、特定时间间隔、特定网络状态等)、设置重试或重复执行等。 3. 通过JobScheduler.schedule()方法将JobInfo对象提交给系统进行调度。系统会根据设置的条件和触发机制来执行任务。 需要注意的是,JobScheduler的保活机制是通过系统的调度来实现的,并不能保证100%的成功保活。因为在一些特殊的情况下,如低内存、电池低、系统启动等情况下,系统可能会暂停或取消JobScheduler的任务。所以我们还需要配合其他的保活机制来提高保活的成功率,如使用前台服务、双进程守护、自启动、推送等。 总结起来,JobScheduler是Android系统提供的一种调度框架,可以在一定程度上保活应用。通过设置JobInfo的相关参数,然后由系统进行调度执行,以确保应用的持续运行和活跃状态。同时,为了提高保活的成功率,我们还需要结合其他的保活机制来综合应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值