Android Q系统JobScheduler原理介绍

Job任务是Android系统上非及时性任务的一种运行机制,由系统根据使用者的条件设置统一管理运行,有条件性,周期性,统一执行,延迟执行等特性;为移动设备性能和功耗带来良好收益。Google自Android M起引入JobScheduler概念,建议非必要alarm操作用该模式替换,让其JobService运行的Job任务满足条件后开始执行,之后Andoroid的每一个版本都有该功能的增添。主要设计思想是懒执行概念,满足必要条件才触发执行。

本文基于Android Q代码介绍。

本文档阅读时间50分钟,黑色字体可作为本文档的简读版本。

1. Job任务执行原理概览

使用者构建Job任务,把其构建信息传递给Job任务管理系统;任务管理系统负责Job任务满足条件的触发,Job任务相应的执行和生命周期管理。

执行模块如下图所示,紫色区域代表Job任务使用者创建模块,红色区域代表Job任务系统管理模块,蓝色区域代表Job任务状态控制模块,绿色区域代表Job任务生命周期管理模块的实现。

执行功能主要由以下四个模块组成,

  • 调用者创建其相关的Job并传递给JobSchedulerService服务,一个Job任务由JobInfo的实例来描述,由唯一ID与JobService的实例一一绑定,通过JobSchedulerService的对外暴露的接口函数schedule来把该Job任务传递到平台层Framework的JobSchedulerService的服务中。
  • Job任务的系统管理模块,JobScheduleService服务是整个Job任务的中枢管理系统,负责管理Job任务的执行和重试,以及管理Job控制模块和Job生命周期控制模块
  • Job任务状态控制模块,当条件状态满足后触发Job的执行。根据系统特性定义了一些Job的系统控制条件,当状态满足控制条件时会更新JobStatus的状态,而后JobSchedulerService服务接到通知去执行Job任务。
  • Job任务生命周期控制模块,该模块是Job调用者需要运行时对其运行状态的控制。该模块通过JobServiceContext类来实现,该类是Job任务与调用者通信的桥梁(可跨进程间调用),处理调用者JobService实例的绑定,启动,执行,停止,完成操作。

2. JobScheduler使用者

调用者使用JobScheduler时,需构建Job任务的相关构造类为JobInfo,其Buider类的参数为JobService的唯一ID和JobService的实现类的ComponentName,调用者在Builder类里设置Job任务的相应满足条件,JobService的实现类包含了Job任务相应的执行或停止函数等实现。
具体实现代码如下例所示,

/** A JobService to clean up obsolete data in anomaly database */

//  定义一个继承JobService的实现类,实现主要功能onStartJob和onStopJob函数
public class AnomalyCleanupJobService extends JobService{
    private static final String TAG = "AnomalyCleanUpJobService";
    @VisibleForTesting
    static final long CLEAN_UP_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);

    public static void scheduleCleanUp(Context context) {
        //获取JobScheduler服务。 
        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
        final ComponentName component = new ComponentName(context, AnomalyCleanupJobService.class);
        // 构建JobInfo.Builder, 为该JobInfo设置满足的执行条件
        final JobInfo.Builder jobBuilder =
                new JobInfo.Builder(R.integer.job_anomaly_clean_up, component)
                        .setPeriodic(CLEAN_UP_FREQUENCY_MS)
                        .setRequiresDeviceIdle(true)
                        .setRequiresCharging(true)
                        .setPersisted(true);

        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_clean_up);
        // Don't schedule it if it already exists, to make sure it runs periodically even after
        // reboot
        // 开启Job任务的执行
        if (pending == null &&jobScheduler.schedule(jobBuilder.build())
                    != JobScheduler.RESULT_SUCCESS) {
                Log.i(TAG, "Anomaly clean up job service schedule failed.");
         }
    }

    @Override
    public boolean onStartJob(JobParameters params){
        final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager
                .getInstance(this);
        final BatteryTipPolicy policy = new BatteryTipPolicy(this);
        ThreadUtils.postOnBackgroundThread(()  -> {
            batteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(
                    System.currentTimeMillis() - TimeUnit.DAYS.toMillis(
                            policy.dataHistoryRetainDay));
              // Job任务执行完成后回调完成通知,设置是否重试
              jobFinished(params, false /* wantsReschedule */);
        })
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters){
        return false;
    }
}

JobInfo.Builder对其Job任务的条件限制大致类别如下:
周期时长,网络状态,时间(延迟时长,最迟运行时长),重试方式,电池信息,contend URI更新等。
具体定义及使用可参考如下文档,文档已清晰介绍,不再过多阐述,
https://developer.android.com/reference/android/app/job/JobInfo.Builder

3. Job任务系统服务管理模块

3.1 Job任务系统管理服务模块简介

Job任务系统管理模块是Job任务的核心模块,系统管理服务是Job任务功能的核心;对使用者起着通讯及同步的作用,担负着任务调用,任务执行状态开启停止及完成;主要管理功能有如下:

  • 控制Job的运行数量JobConcurrencyManager(总体,后台最小,后台最大,根据内存压力配置数量组(ProcessStats.ADJ_MEM_FACTOR_*))。
  • 接收调用者创建的Job,将数据存入到JobStore。
  • 初始化Job的状态控制模块,将各个Job相应的触发条件分别更新到各个状态控制模块。
  • 常量配置,可执行Job数量,权重因子等。
  • 创建平台层Job任务为JobStatus(JobInfo为其构造函数的首参数),平台层以此为来处理和维护Job。
  • Job任务的消息处理,执行,过期,停止;UID状态更新(包含change, gone, active, idle)。

3.2 Job任务系统管理服务初始化流程

主要介绍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任务共同运行

dump里的配置信息如下:

Config={tot=10 bg min/max=2/6} Running[FG/BG (total)]: 0 / 0 (0) Pending: 0 / 0 (0) Actual max: 0 / 0 (0) Res BG: 0 Starting: 0 / 0 (0) Total: 0 / 0 (0)
  • 创建应用待机组的跟踪 (app standby bucket tracker)
  • 初始化各个子状态控制模块
  • 注册time change的receiver,当时间更新后重新计算Job任务的执行时间

3.3 Job任务系统管理服务启动阶段

Job任务系统管理服务的启动主要包含系统服务自身初始化和系统服务开始绑定到三方应用两个流程,

3.3.1 系统服务自身初始化完成 (PHASE_SYSTEM_SERVICES_READY)

  • 初始化子条件状态控制
for (StateController controller : mControllers) {
    controller.onSystemServicesReady();
}
  • 开始注册broadcast receiver来监听package的相关变化(remove, change, restart, query package restart)
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(
    mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
  • 开始注册broadcast receiver来监听uid的相关变化(gone, idle, active, procstate)
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
//JobConcurrencyManager的初始化
mConcurrencyManager.onSystemReady();

public void onSystemReady() {
    mPowerManager = mContext.getSystemService(PowerManager.class);
    final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    mContext.registerReceiver(mReceiver, filter);
    onInteractiveStateChanged(mPowerManager.isInteractive());
}
  • 取消当前无效的Job任务
// Remove any jobs that are not associated with any of the current users.
cancelJobsForNonExistentUsers();

3.3.2 系统服务开始绑定到三方应用 (PHASE_THIRD_PARTY_APPS_CAN_START)

  • 获取batterystats服务和DeviceIdleController
  mReadyToRock = true;
  mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
  BatteryStats.SERVICE_NAME));
  mLocalDeviceIdleController
     = LocalServices.getService(DeviceIdleController.LocalService.class);
  • 创建MAX_JOB_CONTEXTS_COUNT个JobServiceContext,添加到mActiveServices JobServiceContext表达一个job的生命周期,每个job对应该类的一个实例,该构造中与JobPackageTracker和BatteryStats相关
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
    mActiveServices.add(
        new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
    getContext().getMainLooper()));
}
  • 把每个job任务在子条件状态控制里做跟踪
mJobs.forEachJob((job) -> {
    for (int controller = 0; controller < mControllers.size(); controller++) {
        final StateController sc = mControllers.get(controller);
    sc.maybeStartTrackingJobLocked(job, null);
   }
});

  • 检查job, 开启job工作模式
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();

3.4 系统管理服务对Job任务的处理流程

JobSchedulerService是该模块的主要实现类,其工作流程为,

  • JobScheduler将JobInfo实例(参数为Job ID和JobService的实例)注册到JobSchedulerService服务中
  • 由JobSchedulerService服务维护Job列表JobStore,JobStore中所存储的Job会存入PendingJobs的列表里
  • Job设置的条件被相应的子StateController解读并注册
  • 子StateController中的状态被满足时会把相应的jobstatus中的满足的限制条件设置为true, 并发送检查job的消息给JobSchedulerService的
  • 当Job任务的条件全部满足时把Job存入PendingJobs的以时间为序的队列里JobConcurrencyManager触发JobServiceContext去执行相关的Job, 开启Job生命周期的管理模块

JobSchedulerService的Job任务执行流程如下图所示,想要详细了解的同学可根据代码和如下流程自行查看。

4. Job任务条件状态控制模块

条件控制模块是Job任务条件满足的涌动机,主要作用是Job任务状态的管理控制功能 ,把调用者Job任务构建满足条件设置到相应子状态控制模块(StateController的子类)中去,随着Android版本的不断升级,该子模块的功能不断被扩展添加。每个条件状态控制会去接受系统的广播信息,由此根据系统广播状态的变化信息来更新各个Job任务的满足条件。

随着系统版本的升级Job任务的状态控制模块在各个Android版本上演进如下图所示。

4.1 子条件状态控制模块(StateController的子类)

Job任务在初始化时,使用者对Job任务的条件设置通过条件位标记的方法赋值给Job任务的请求条件变量(JobStatus的mRequiredConstraints),其设置条件会对应设置到StateController的子类的条件状态中,Job任务主动设置到状态控制模块里的条件称为显式满足条件,状态控制模块为Job任务添加的条件称为隐式满足条件。

该模块的处理流程为,子条件状态控制模块来监控相关状态(注册BroadcastReceiver),状态发生变更后会更新Job任务的满足条件;某个条件满足会设置相应的Job任务的满足条件变量的某个条件标记位为1(JobStatus的satisfiedConstraints变量的某指定条件位设置为1),当一个Job任务所有的条件都满足后(满足条件为mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied || mRequiredConstraintsOfInterest == satisfiedConstraints));最后由JobSchedulerService去触发执行Job任务。

4.1.1 显式满足条件的子状态控制模块

使用者主动设置的条件称为显示满足条件,显示满足条件会主动一一映射到各子条件状态控制模块,主要的显示满足相应子条件控制模块有,

  • 网络连接状态控制模块,Job任务的网络类型及连接条件由ConnectivityController来实现,ConnectivityController来监控网络的连接情况。
  • 执行时间状态控制模块,针对Job任务的下次到期的时间设置alam提醒,该条件由TimeController实现。
  • 系统闲时状态控制模块,条件由IdleController来监控系统闲时的进入和退出,闲时定义为充电状态下灭屏后71min。
  • 电池状态控制模块,条件由BatteryController来实现,其Job任务设置条件有正在充电和电池非低电情况。
  • 存储空间状态控制模块,条件由StorageController来监控存储空间是否处于低位。
  • 数据库更新状态控制模块,Contenet URI更新条件由ContentObserverController来实现,监控Job任务设置的数据库是否更新。

4.1.2 三个隐式满足条件的子状态控制模块

有三个条件为系统自动为Job任务强制添加,使用者无法设置该类条件,其他条件均可被使用者设置,
该三个条件是:

  • Doze限制执行(DEVICE_NOT_DOZING),设备Doze状态下不允许执行Job任务,前台和白名单应用除外,该条件被DeviceIdleJobsController子条件状态控制。
  • 后台限制执行(BACKGROUND_NOT_RESTRICTED),根据AppStandby机制对后台应用的Job任务进行限制,该条件被BackgroundJobsController子条件状态控制。
  • 应用待机组(WITHIN_QUOTA),应用待机组条件控制,依照各待机组类别做相应的限制,该条件被QuotaController子条件状态控制。四类应用待机组对Job的有相关限制,活跃组无限制,工作级组在两个小时允许执行10分钟,频繁使用组八个小时允许执行10分钟,非常用组二十四小时允许执行10分钟。

4.2 条件状态控制模块属性

条件状态控制模块属性分为常驻属性和可选属性两类,常驻属性表明在该子条件状态控制模块必须实现这些属性,该属性是所有子条件状态控制模块共有的特性;可选属性意思则为按需加载实现

4.2.1. 常驻属性

所有子状态控制模块都需要实现该属性:

  • 开启任务状态控制,每向JSS中加入一个Job,都会调用maybeStartTrackingJobLocked()方法开始记录跟踪此Job;
  • 停止任务状态控制,当Job任务执行完成或被取消时,都会调用maybeStopTrackingJobLocked()方法停止此Job的记录。

4.2.2 可选属性

子状态控制模块有选择的实现该属性:

  • 重新调度失败的任务 rescheduleForFailureLocked (ContentObserverController)
  • 常量或白名单的更新 onConstantsUpdatedLocked (ConnectivityController, QuotaController)
    ConnectivityController: 在app idle里设置白名单为不允许
    QuotaController:根据App的Bucket来更新任务的Quota状态
  • 准备可运行的任务 prepareForExecutionLocked (ContentObserverController, QuotaController)
    ContentObserverController:为即将运行的Job任务准备好URI。
    QuotaController:将非TOP应用添加到追踪列表中。
  • 应用删除后的清理 onUserRemovedLocked (ConnectivityController, QuotaController)
  • 用户删除后的清理 onAppRemovedLocked (QuotaController)
  • 评估任务状态 (ConnectivityController, TimerController)
    ConnectivityController: 评估是否网络连接的任务
    TimerController: 评估最迟运行(deadine)时间(latestRunTimeElapsedMillis)是否到期

5. Job任务生命周期控制模块

5.1 Job任务生命周期介绍

Job任务的运行流程Job任务的执行状态来表示的,可称之为Job任务的生命周期处理,由状态机来表现,JobServiceContext是该模块的一个主要逻辑类,承担Job任务的生命周期的处理和与使用者的Job任务执行状态同步的作用。
JobServiceContext实例与Job任务的生命周期为一一对应,将JobServiceContext绑定到使用者的JobService上,绑定过程中开启PowerManager.PARTIAL_WAKE_LOCK操作, Job任务完成后释放该WAKE_LOCK,其主要目的是通知使用者执行Job任务的开启执行或停止等操作,当使用者被开启任务的执行时,完成Job任务后会发起主动通知到JobServiceContext。

每一个Job任务的初始状态由完成(Finishing)状态开始,一个成功运行的Job任务执行状态转换按绑定状态(binding)->开启状态(starting)->执行状态(executing)->完成状态(finishing)的流程来完成状态的切换。

Job任务执行状态流程如下图所示,绿色代表Job任务生命周期状态处理流程,紫色代表与使用者Job任务执行状态传递及返回,红色代表Job任务停止状态处理流程。

5.2 超时机制处理机制

JobServiceContext拥有一个执行状态超时处理机制,由于JobServiceContext与其目的是为了与使用者的调用建立一个容错能力,如果在约定时间内未完成操作就会触发超时状态的切换及清理工作,各个执行状态的超时时间设置为绑定状态超时18s,开启状态超时8s,执行状态超时600s;当绑定和开启状态超时被触发后执行状态切换到完成状态,去运行Job任务的清除动作

5.3 停止状态的处理机制

当在执行状态下触发超时,切换到停止状态,调用使用者的停止函数,执行停止成功后再把状态切换到完成状态。使用者中的JobServiceEngine由三个消息(MSG_EXECUTE_JOB, MSG_STOP_JOB, MSG_JOB_FINISHED)来构建执行流程, 其中执行和停止操作由JobServiceContext主动触发,而完成操作是被动触发JobServiceContext

5.4 状态取消处理机制

执行状态失败处理与超时机制的处理逻辑是一致的,一旦触发取消操作,绑定和开启状态切换到完成状态做后续的清理工作;而执行状态下的取消操作,会切换到停止状态,调用使用者的停止函数,执行停止成功后再把状态切换到完成状态

5.5 Job任务的清除

完成状态(Finishing)后的统一清除。设置状态为完成状态,解除使用者的JobService绑定,由系统统一清理未绑定的服务;并通知JobSchedulerService该Job已完成,停止继续检测该Job管理状态

5.6 设计要点

  • JobServiceContext实现与使用者通讯不返回的timeout容错机制**
  • 执行状态同步到使用者分为主动状态更新和被动状态更新两类,除完成状态更新为被动状态以外其他状态皆为JobServiceContext主动设置**
  • 执行状态的最后结束状态皆为完成状态,统一完成任务的清除操作**

作者:林芊_lqsohu
链接:https://www.jianshu.com/p/e1b4d952c79f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容