1、概述
在Android开发过程中可能会遇到这样需求,需要系统处于某一种或者多种特定的条件下才会开始执行某一项任务,比如在充电、wifi的情况下才会开始下载任务。因此在Android5.0及以上Google为我们提供了JobScheduler策略方案用于处理这种情况,同时也是为了达到省电的目的。
2、初识JobScheduler——使用方式
JobScheduler的使用可以分为如下几步走:
(1)新建Service并实现JobService,同时实现JobService中的onStartJob和onStopJob方法用于实现开始和结束执行任务代码;
public class MyJobService extends JobService {
public MyJobService() {
}
/**
* return false:系统会认为该任务已经执行完毕,且不是一个耗时的操作
* return true:系统会认为执行的是一个耗时的操作,当该方法执行完毕的时候,系统会认为任务仍然在异步执行,
* 当任务执行完毕之后,必须手动调用jobFinished(xx,xx)方法,如果不调用系统会一直认为任务在执行,那么就
* 会阻塞导致其他任务不能被执行
**/
@Override
public boolean onStartJob(JobParameters params) {
//任务实现
return false;
}
/**
* 当系统接收到一个取消请求时,系统会调用该方法取消正在执行的任务,只有在onStartJob返回true
* 的时候才会被调用(在后面的源码解析中会知道为什么)
* return:true代表需要重新执行任务,false则不需要;但是并没有实际的作用。这个在文章最后会进行讲解。
**/
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
(2)通过getSystemService获取JobSchedulerImpl(JobScheduler的实现类)实例;
(3)通过JobInfo中的内部类Builder构造JobInfo实例;
(4)调用JobSchedulerImpl中的方法将任务递交给JobSchedulerService进行处理。
JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(getPackageName(),MyJobService.class.getName());
JobInfo jobInfo = new JobInfo.Builder(JOBID,componentName)
.setMinimumLatency(1000)
xx //其他条件
.build();
mJobScheduler.schedule(jobInfo);
3、再识JobScheduler——源码分析
在应用层通过context.getSystemService的方式获取到系统服务的实例对象,而该方法的真正实现则是在ContextImpl中,进入到ContextImpl的getSystemService方法中会发现最后会调用到SystemServiceRegistry中的getSystemService方法中,在该类的getSystemService方法中则会返回JobSchedulerImpl实例对象,因此JobSchedulerService的接口并不会直接提供给用户使用,而是通过JobSchedulerImpl对象间接的供用户使用。所以源码分析需要从JobSchedulerImpl源码入手。
3.1 JobSchedulerImpl源码
看JobScheduler源码会发现它只是一个抽象类而已,真正的实现类是在JobSchedulerImpl中,源码如下:
public class JobSchedulerImpl extends JobScheduler {
IJobScheduler mBinder;
JobSchedulerImpl(IJobScheduler binder) {
mBinder = binder;
}
//将一个任务加入任务列表中,如果在任务队列中存在相同JobId的任务则会替换旧的Job,如果该Job正在运行中,则会停止该任务
@Override
public int schedule(JobInfo job) {
try {
return mBinder.schedule(job);
} catch (RemoteException e) {
return JobScheduler.RESULT_FAILURE;
}
}
//将任务加入到任务列表中,但是允许将work添加到新的或者已经存在的Job中
//如果该任务在任务列表中存在和当前任务jobId相同的Job则会替换掉原来的Job
@Override
public int enqueue(JobInfo job, JobWorkItem work) {
try {
return mBinder.enqueue(job, work);
} catch (RemoteException e) {
return JobScheduler.RESULT_FAILURE;
}
}
//类似于schedule,最后都会调用到JobSchedulerService中的scheduleAsPackage方法中
@Override
public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) {
try {
return mBinder.scheduleAsPackage(job, packageName, userId, tag);
} catch (RemoteException e) {
return JobScheduler.RESULT_FAILURE;
}
}
//取消一个任务,如果当前任务正在执行,那么会马上停止该任务并且该JobScheduler对应的JobService中的onStopJob的
//返回值会无效
@Override
public void cancel(int jobId) {
try {
mBinder.cancel(jobId);
} catch (RemoteException e) {}
}
//取消所有任务
@Override
public void cancelAll() {
try {
mBinder.cancelAll();
} catch (RemoteException e) {}
}
//获取所有计划执行的任务
@Override
public List<JobInfo> getAllPendingJobs() {
try {
return mBinder.getAllPendingJobs();
} catch (RemoteException e) {
return null;
}
}
//获取对应JobId的任务信息
@Override
public JobInfo getPendingJob(int jobId) {
try {
return mBinder.getPendingJob(jobId);
} catch (RemoteException e) {
return null;
}
}
}
很明显在JobSchedulerImpl中并没有对数据做任何处理,只是提供了添加任务、取消任务等方法,并在这些方法中通过Binder的方式调用到JobSchedulerService中对数据进行处理,在理解JobSchedulerService源码之前还需要理解几个辅助类才行。
3.2 JobInfo源码
JobInfo的实例对象是通过其中的内部类Builder构造出来的,因此理解JobInfo只需要理解其中的Builder类就Ok了。部分代码如下:
public static final class Builder {
...........//省略其中的成员变量
public Builder(int jobId, @NonNull ComponentName jobService) {
mJobService = jobService;
mJobId = jobId;
}
...........//省略其中被隐藏的以及设置参数的方法
/**
* 设置需要的网络状态:
* NETWORK_TYPE_NONE:不需要网络,默认;
* NETWORK_TYPE_ANY:需要网络连接;
* NETWORK_TYPE_UNMETERED:需要不是蜂窝数据连接
* NETWORK_TYPE_NOT_ROAMING:未漫游的网络连接
* NETWORK_TYPE_CELLULAR:蜂窝数据网络连接
*/
public Builder setRequiredNetworkType(@NetworkType int networkType) {
...........
}
//设置该任务可能会下载和上传的数据量
public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
@BytesLong long uploadBytes) {
mNetworkDownloadBytes = downloadBytes;
mNetworkUploadBytes = uploadBytes;
return this;
}
//是否需要充电才允许被执行,默认是false
public Builder setRequiresCharging(boolean requiresCharging) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
| (requiresCharging ? CONSTRAINT_FLAG_CHARGING : 0);
return this;
}
//是否在非低电量情况下才允许被执行,默认是false
public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
| (batteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
return this;
}
//设置是否在设备进入到idle状态下(没有使用设备一段时间)
//任务才会被执行(在设备为active状态时不会被执行),默认是false
public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
| (requiresDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
return this;
}
//是否在非内存低的情况下才允许被执行,默认是false(不允许)
public Builder setRequiresStorageNotLow(boolean storageNotLow) {
mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
| (storageNotLow ? CONSTRAINT_FLAG_STORAGE_NOT_LOW : 0);
return this;
}
/**
* 设置需要监听的ContentProvider中uri对应的内容是否发生变化,如果发生变化则开始
* 执行任务
*/
public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
if (mTriggerContentUris == null) {
mTriggerContentUris = new ArrayList<>();
}
mTriggerContentUris.add(uri);
return this;
}
//设置检测ContentProvider中uri对应内容变化到安排作业执行的延迟
public Builder setTriggerContentUpdateDelay(long durationMs) {
mTriggerContentUpdateDelay = durationMs;
return this;
}
public Builder setTriggerContentMaxDelay(long durationMs) {
mTriggerContentMaxDelay = durationMs;
return this;
}
//设置任务可循环执行,并在每一个周期内最多可执行一次
public Builder setPeriodic(long intervalMillis) {
return setPeriodic(intervalMillis, intervalMillis);
}
public Builder setPeriodic(long intervalMillis, long flexMillis) {
.............
}
//延迟执行任务,该方法不能和setPeriodic同时调用,否则会抛IllegalArgumentException异常
public Builder setMinimumLatency(long minLatencyMillis) {
mMinLatencyMillis = minLatencyMillis;
mHasEarlyConstraint = true;
return this;
}
//设置最大的延迟执行任务时间,也不能和setPeriodic同时调用
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
mMaxExecutionDelayMillis = maxExecutionDelayMillis;
mHasLateConstraint = true;
return this;
}
/**
* 设置退避/重试策略
* initialBackoffMillis:第一次尝试重试的等待间隔时间
* backoffPolicy:对应的退避策略,比如等待时间呈指数型增长
*/
public Builder setBackoffCriteria(long initialBackoffMillis,
@BackoffPolicy int backoffPolicy) {
final long minBackoff = getMinBackoffMillis();
if (initialBackoffMillis < minBackoff) {
Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
+ mJobId + " is too small; raising to " + formatDuration(minBackoff));
initialBackoffMillis = minBackoff;
}
mBackoffPolicySet = true;
mInitialBackoffMillis = initialBackoffMillis;
mBackoffPolicy = backoffPolicy;
return this;
}
//设置为true表示该任务很重要,那么当该任务对应的应用处于前台或者处于doze临时白名单中,当该应用执行job的时候系统则会放宽对该应用的doze限制,默认为false
public Builder setImportantWhileForeground(boolean importantWhileForeground) {
if (importantWhileForeground) {
mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
} else {
mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND);
}
return this;
}
//在系统重启之后任务是否继续执行
@RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
public Builder setPersisted(boolean isPersisted) {
mIsPersisted = isPersisted;
return this;
}
//生成JobInfo实例对象
public JobInfo build() {
// Allow jobs with no constraints - What am I, a database?
if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 &&
mNetworkRequest == null &&
mTriggerContentUris == null) {
throw new IllegalArgumentException("You're trying to build a job with no " +
"constraints, this is not allowed.");
}
// Check that network estimates require network type
if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
throw new IllegalArgumentException(
"Can't provide estimated network usage without requiring a network");
}
// We can't serialize network specifiers
if (mIsPersisted && mNetworkRequest != null
&& mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
throw new IllegalArgumentException(
"Network specifiers aren't supported for persistent jobs");
}
// Check that a deadline was not set on a periodic job.
if (mIsPeriodic) {
if (mMaxExecutionDelayMillis != 0L) {
throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
"periodic job.");
}
if (mMinLatencyMillis != 0L) {
throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
"periodic job");
}
if (mTriggerContentUris != null) {
throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
"periodic job");
}
}
if (mIsPersisted) {
if (mTriggerContentUris != null) {
throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
"persisted job");
}
if (!mTransientExtras.isEmpty()) {
throw new IllegalArgumentException("Can't call setTransientExtras() on a " +
"persisted job");
}
if (mClipData != null) {
throw new IllegalArgumentException("Can't call setClipData() on a " +
"persisted job");
}
}
if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) {
throw new IllegalArgumentException("An important while foreground job cannot "
+ "have a time delay");
}
if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
throw new IllegalArgumentException("An idle mode job will not respect any" +
" back-off policy, so calling setBackoffCriteria with" +
" setRequiresDeviceIdle is an error.");
}
return new JobInfo(this);
}
}
从上面的JobInfo中的Builder类可以知道在添加Job的时候可以设置在什么条件下才可以执行,例如:设置网络条件、是否充电等。
3.3 JobWorkItem源码
在JobScheduler源码中有一个enqueue方法,其中一个参数的实例对象是JobWorkItem,部分源码如下:
final public class JobWorkItem implements Parcelable {
final Intent mIntent;
final long mNetworkDownloadBytes;
final long mNetworkUploadBytes;
int mDeliveryCount;
int mWorkId;
Object mGrants;
public JobWorkItem(Intent intent) {
mIntent = intent;
mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
}
public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
mIntent = intent;
mNetworkDownloadBytes = downloadBytes;
mNetworkUploadBytes = uploadBytes;
}
.....................
}
会发现该类就是一个实体类,用于存储需要被执行的intent以及可能会使用到的上传和下载流量。
3.4 JobSchedulerService源码
在JobSchedulerImpl源码中没有对数据做任何的处理,只是通过Binder的方式将数据传递到了JobSchedulerService中进行处理。因此,如果需要真正理解JobScheduler的源码必须理解JobSchedulerService才行。
JobSchedulerService继承SystemService类,并在SystemServer中进行启动,时序图如下所示:

3.4.1 onstart方法
//将当前Service添加到ServiceManager中
@Override
public void onStart() {
publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
}
3.4.2 JobSchedulerStub源码
从上面的分析知道在JobSchedulerImpl中是通过Binder的方式调用到了JobSchedulerService中,而真正的实现类则是其内部类JobSchedulerStub。所以最后的实现都入口则是在JobSchedulerStub中。部分源码如下:
@Override
public int schedule(JobInfo job) throws RemoteException {
//获取该任务对应的uid、pid以及userId
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
//用于检测是否是应用自身设置的任务以及在应用中的配置文件中是否
//设置了BIND_JOB_SERVICE permission权限
enforceValidJobRequest(uid, job);
//判断该应用是否有RECEIVE_BOOT_COMPLETED权限
if (job.isPersisted()) {
if (!canPersistJobs(pid, uid)) {
throw new IllegalArgumentException("Error: requested job be persisted without"
+ " holding RECEIVE_BOOT_COMPLETED permission.");
}
}
.......
long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
...........//判断数据的合法性以及是否设置了对应权限
long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
throws RemoteException {
final int callerUid = Binder.getCallingUid();
............//检查数据的合法性以及是否设置了相应的权限
long ident = Binder.clearCallingIdentity();
try {
return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
packageName, userId, tag);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
................//其他方法
3.4.2 scheduleAsPackage
很明显JobSchedulerStub中的方法也只是一个入口而已,真正的实现还是在JobSchedulerService中,并且在JobSchedulerImpl中的三个添加任务的方法最终实现都在JobSchedulerService的scheduleAsPackage方法中。源码如下:
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
synchronized (mLock) {
//从列表中查找是否存在和当前Job的JobId相同的Job
final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
//如果work != null(通过enqueue的方式添加任务)且在列表中存在和当前任务相同uid的Job
if (work != null && toCancel != null) {
if (toCancel.getJob().equals(job)) {
//将work添加到旧的任务队列中
toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
return JobScheduler.RESULT_SUCCESS;
}
}
//将当前任务封装为JobStatus
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
...................
// 准备执行,将prepare变量置为true
jobStatus.prepareLocked(ActivityManager.getService());
//如果任务列表中存在和当前任务JobId相同的Job则取消旧的任务
if (toCancel != null) {
cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
}
//如果是通过enqueue的方式添加任务,并且work != null则将work添加到新的Job中
if (work != null) {
jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
}
//启动Job
startTrackingJobLocked(jobStatus, toCancel);
//判断Jo是否需要马上执行,如果需要马上执行则会尽快执行该任务
//如果不需要则会等待该任务设置的条件所对应的Controller状态变化来确认该任务是否执行
if (isReadyToBeExecutedLocked(jobStatus)) {
mJobPackageTracker.notePending(jobStatus);
//将job添加到mPendingJobs中(通过二分法查找下标)
addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
//通过JobServiceContext执行一个任务(调用到JobService中的onStartJob中)
maybeRunPendingJobsLocked();
}
}
return JobScheduler.RESULT_SUCCESS;
}
public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, String tag) {
//获取系统开机时间(包括待机时间)
final long elapsedNow = SystemClock.elapsedRealtime();
final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
//判断该任务是否是周期执行
if (job.isPeriodic()) {
//下一次执行时间
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
//下一次最早的执行时间
//getFlexMillis获取周期性任务可执行时间段(也就是该任务在该时间段内的任意时间点都可以执行)
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
} else {
//该job是否设置了最早执行时间,如果是则最早执行时间是:
//当前时间加上设置的最早执行时间
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
//该job是否设置了最晚执行时间,如果是则最晚执行时间是:
//当前时间加上设置的最晚执行时间
latestRunTimeElapsedMillis = job.hasLateConstraint() ?
elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
}
String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();
//应用待机群组,该功能是Android9.0为降低功耗引入的应用管理功能
//获取设置任务的应用属于应用待机群组的哪一个级别
int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
sourceUserId, elapsedNow);
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
long currentHeartbeat = js != null
? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
: 0;
return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
standbyBucket, currentHeartbeat, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
/*innerFlags=*/ 0);
}
private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
//将Job的状态从准备执行变更为未准备的状态
cancelled.unprepareLocked(ActivityManager.getService());
//从集合中移除Job
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
// Remove from pending queue.
if (mPendingJobs.remove(cancelled)) {
mJobPackageTracker.noteNonpending(cancelled);
}
// 如果任务正在运行则停止执行
stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
reportActiveLocked();
}
private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
//获取正在运行的任务
final JobStatus executing = jsc.getRunningJobLocked();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
//将任务取消运行
jsc.cancelExecutingJobLocked(reason, debugReason);
return true;
}
}
return false;
}
private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
//Job是否已经准备执行
if (!jobStatus.isPreparedLocked()) {
Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
}
//获取系统从开机到当前的时间(包括睡眠时间)
jobStatus.enqueueTime = SystemClock.elapsedRealtime();
//将当前的Job添加到列表中,最后是通过JobStore中的内部类JobSet中的add方法添加到对应
//应用uid的集合中,如果该Job是重启继续执行的,则将所有添加到xml文件中,在JobStore的
//构造方法中会从文件中重新读取数据并存储到Job列表中
final boolean update = mJobs.add(jobStatus);
//mReadyToRock=true表示应用可以通过Binder的方式和Service进行交互
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
//将Job添加到对应的controller集合中
//controller在JobSchedulerService的构造方法中启动,用于监听设备所处的几种状态
//当系统处于某种状态时,如果Job处于该种状态那么就会执行该Job
StateController controller = mControllers.get(i);
if (update) {
controller.maybeStopTrackingJobLocked(jobStatus, null, true);
}
controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
}
}
}
public boolean add(JobStatus job) {
final int uid = job.getUid();
final int sourceUid = job.getSourceUid();
ArraySet<JobStatus> jobs = mJobs.get(uid);
if (jobs == null) {
jobs = new ArraySet<JobStatus>();
//一个应用uid对应一个Job列表集合
mJobs.put(uid, jobs);
}
ArraySet<JobStatus> jobsForSourceUid = mJobsPerSourceUid.get(sourceUid);
if (jobsForSourceUid == null) {
jobsForSourceUid = new ArraySet<>();
mJobsPerSourceUid.put(sourceUid, jobsForSourceUid);
}
final boolean added = jobs.add(job);
final boolean addedInSource = jobsForSourceUid.add(job);
if (added != addedInSource) {
Slog.wtf(TAG, "mJobs and mJobsPerSourceUid mismatch; caller= " + added
+ " source= " + addedInSource);
}
//是否添加成功
return added || addedInSource;
}
经过从应用层如何将Job添加到框架层这个整个过程的分析,总算是把其中的很多细节给理清楚了。大致过程如下:
(1)当上层构造的job传递到JobSchedulerService中的时候首先会判断数据的合法性以及是否添加的相应的权限;
(2)尝试获取在job列表中是否存在和当前Job具有相同JobIdJob并赋值给toCancel;
(3)判断toCancel和work是否为null,如果不为null并且当前Job实例和toCancel实例相同则直接将当前Job的work直接添加到toCancel队列中,然后直接返回RESULT_SUCCESS;
(4)将新的Job构造成JobStatus,并准备执行该Job;
(5)如果toCancel!=null则取消旧的Job,在这个过程中会将旧Job上面的work转移到新的Job上;
(6)如果work != null则将该work添加到新Job的队列中;
(7)将Job添加到Controller实例的成员变量集合中;
(8)判断当前Job是否需要马上执行,如果需要则尽快执行该Job;
(9)返回RESULT_SUCCESS表示添加Job成功;
(10)等待Controller状态变化,以判断Job是否满足所预置的条件。
4、Job的执行过程
从上面的源码分析知道如果一些Job需要立马执行,那么系统会将该Job直接添加到pendingJobs list中并尽快的去执行,并且这些Job的延迟执行时间为0。但是对于一般的任务都需要满足自己设定的某一些条件之后才能执行,比如电量不能太低、充电、联网等,而这些条件在JobSchedulerService中都是通过对应的Controller来实现的。因此下面会对其中的部分Controller进行讲解。
4.1 BatteryController源码
public final class BatteryController extends StateController {
..............
private BatteryController(StateChangedListener stateChangedListener, Context context,
Object lock) {
//调用父类构造方法
super(service);
//生成广播接收实例
mChargeTracker = new ChargingTracker();
//为广播接收添加filter
mChargeTracker.startTracking();
}
@Override
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
//判断job是否有非低电量或者充电限制
if (taskStatus.hasPowerConstraint()) {
//将job添加到追踪列表中
mTrackedTasks.add(taskStatus);
//给job添加Battery Controller追踪器
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
//设置充电且非低电量约束(Job执行条件)
taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower());
//设置非低电量约束(Job执行条件)
taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow());
}
}
@Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
//从job中移除battery Controller追踪器
if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
mTrackedTasks.remove(taskStatus);
}
}
/**
* 满足条件则执行任务,但是充电和非低电量情况是一起处理的,那么现在需要考虑如下几种情况
* 1、需要满足充电才能执行任务:
* (1)充电且电池处于低电量(不存在该种情况)
* (2)充电且电池电量健康
* 2、需要满足电池非低电量才能执行:
* (1)非充电且电池电量健康
* (2)充电且电池电量健康
**/
private void maybeReportNewChargingStateLocked() {
//是否正在充电且非低电量
final boolean stablePower = mChargeTracker.isOnStablePower();
//是否是非低电量
final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
....................
boolean reportChange = false;
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
//job是否已经设置了该约束条件,如果没有则设置
boolean previous = ts.setChargingConstraintSatisfied(stablePower);
if (previous != stablePower) {
reportChange = true;
}
previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow);
if (previous != batteryNotLow) {
reportChange = true;
}
}
//mStateChangedListener是JobSchedulerService实例对象
if (stablePower || batteryNotLow) {
mStateChangedListener.onRunJobNow(null);
} else if (reportChange) {
mStateChangedListener.onControllerStateChanged();
}
}
public final class ChargingTracker extends BroadcastReceiver {
..................
public void startTracking() {
//注册filter
.......................
BatteryManagerInternal batteryManagerInternal =
LocalServices.getService(BatteryManagerInternal.class);
mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
................
@Override
public void onReceive(Context context, Intent intent) {
onReceiveInternal(intent);
}
@VisibleForTesting
public void onReceiveInternal(Intent intent) {
synchronized (mLock) {
final String action = intent.getAction();
if (Intent.ACTION_BATTERY_LOW.equals(action)) {
.............
//接收到该action说明设备没有充电
mBatteryHealthy = false;
maybeReportNewChargingStateLocked();
} else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
.................
mBatteryHealthy = true;
maybeReportNewChargingStateLocked();
} else if (BatteryManager.ACTION_CHARGING.equals(action)) {
.................
mCharging = true;
maybeReportNewChargingStateLocked();
} else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
................
mCharging = false;
maybeReportNewChargingStateLocked();
}
mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE,
mLastBatterySeq);
}
}
}
}
//setChargingConstraintSatisfied方法最终会调用到该处
boolean setConstraintSatisfied(int constraint, boolean state) {
//是否已经设置了约束条件
boolean old = (satisfiedConstraints&constraint) != 0;
if (old == state) {
return false;
}
//设置最新的约束条件,state=true(充电且非低电量或者非低电量)
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
return true;
}
阅读所有的Controller源码会发现大多数的实现方式都很相似,通过在构造函数中设置监听器或者注册广播的方式来获取相应的条件状态;并将Job添加到当前的Controller追踪列表中,同时为该Job添加当前Controller追踪器。当条件满足后会调用到JobSchedulerService中的onControllerStateChanged或者onRunJobNow方法中。
@Override
public void onControllerStateChanged() {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
@Override
public void onRunJobNow(JobStatus jobStatus) {
mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
}
final private class JobHandler extends Handler {
public JobHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
synchronized (mLock) {
if (!mReadyToRock) {
return;
}
switch (message.what) {
case MSG_JOB_EXPIRED: {
JobStatus runNow = (JobStatus) message.obj;
//如果立马执行的Job不为null,则判断该Job是否已经准备好,如果已经准备好则将该Job添加到待执行队列中
if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
mJobPackageTracker.notePending(runNow);
addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
} else {
//将所有满足条件的Job添加到待执行队列中
queueReadyJobsForExecutionLocked();
}
} break;
//检查Job是否满足执行条件,并添加到待执行队列中
case MSG_CHECK_JOB:
//当前系统处于运行状态(非Doze状态等)
if (mReportedActive) {
//首先会清除掉待执行队列中的所有Job,并停止执行不满足执行条件的Job,然后遍历列表中的Job将满足执行条件的Job添加到待执行队列mPendingJobs中,并按Job进入时间进行排序
queueReadyJobsForExecutionLocked();
} else {
//如果当前列表中已经满足执行条件的Job到达一定数量之后则会将这些Job添加到待执行队列中,并根据job进入的时间进行排序
maybeQueueReadyJobsForExecutionLocked();
}
break;
case MSG_CHECK_JOB_GREEDY:
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null,
"app no longer allowed to run");
break;
}
maybeRunPendingJobsLocked();
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
removeMessages(MSG_CHECK_JOB);
}
}
}
Controller中调用onControllerStateChanged方法之后则会检测哪些Job已经满足了执行条件并将已经满足执行条件的Job添加到待执行列表中,而调用onRunJobNow方法则会将所有已经准备好的Job添加到待执行列表中,最后通过调用JobServiceContext中的executeRunnableJob方法通过Binder的方式执行JobService中的onStartJob方法。
private void maybeRunPendingJobsLocked() {
//(1)获取当前系统的内存级别用于判断同时可处理的Job数量;
//(2)获取当前正处于运行状态的任务数量;
//(3)根据Job优先级获取需要被执行的任务;
//(4)调用JobServiceContext中的executeRunnableJob方法执行任务
assignJobsToContextsLocked();
//是否有活动正在运行
reportActiveLocked();
}
boolean executeRunnableJob(JobStatus job) {
synchronized (mLock) {
.............
mRunningJob = job;
mRunningCallback = new JobCallback();
//该任务执行时间是否过期
final boolean isDeadlineExpired =
job.hasDeadlineConstraint() &&
(job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
.............
final JobInfo ji = job.getJob();
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
isDeadlineExpired, triggeredUris, triggeredAuthorities);
mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
mVerb = VERB_BINDING;
scheduleOpTimeOutLocked();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
//通过Binde的方式启动自定义的JobService,并且当前类实现了ServiceConnection
boolean binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
new UserHandle(job.getUserId()));
.....................
return true;
}
}
public void onServiceConnected(ComponentName name, IBinder service) {
JobStatus runningJob;
synchronized (mLock) {
runningJob = mRunningJob;
//停止Job
if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
closeAndCleanupJobLocked(true /* needsReschedule */,
"connected for different component");
return;
}
this.service = IJobService.Stub.asInterface(service);
//获取wakelock防止系统休眠
final PowerManager pm =
(PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
runningJob.getTag());
wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
wl.setReferenceCounted(false);
wl.acquire();
if (mWakeLock != null) {
mWakeLock.release();
}
mWakeLock = wl;
doServiceBoundLocked();
}
}
void doServiceBoundLocked() {
removeOpTimeOutLocked();
handleServiceBoundLocked();
}
private void handleServiceBoundLocked() {
//可能没有Bind成功,直接cancel当前Job
if (mVerb != VERB_BINDING) {
closeAndCleanupJobLocked(false /* reschedule */, "started job not pending");
return;
}
//当job在等待Binder完成的时候被cancel了,直接cancel当前Job
if (mCancelled) {
closeAndCleanupJobLocked(true /* reschedule */, "cancelled while waiting for bind");
return;
}
try {
mVerb = VERB_STARTING;
scheduleOpTimeOutLocked();
//会调用到JobServiceEngine中Binder实现类s的tartJob方法;
//在startJob方法中调用其内部类JobHandler;
//在JobHandler中调用onStartJob方法最后调用到自定义的JobService中
//看到这里终于知道了为什么JobService是运行在主线程中了,因为就是在主线程中调用的onStartJob方法
service.startJob(mParams);
} catch (Exception e) {
Slog.e(TAG, "Error sending onStart message to '" +
mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
}
}
呼~,撸到这里总算是把Job的执行流程基本上是理清楚了,但是还有一个疑问:如果JobService中返回的false表示这个Job是非耗时的,那么该Job如果不是循环执行的到这里就结束了,后面就不会出现该Job了,也就是系统会将该Job从Job列表中移除掉;那么是怎样移除的呢?首先需要知道的是通过Binder的方式启动JobService的时候,首先会调用JobService中的onBinder方法获取binder对象。在JobService中的onBinder方法中做了如下处理:
public final IBinder onBind(Intent intent) {
if (mEngine == null) {
//生成新的JobServiceEngine对象,在该类中存在和JobService相同的两个方法,并在这两个方法中分别调用了当前JobService的对应方法
mEngine = new JobServiceEngine(this) {
@Override
public boolean onStartJob(JobParameters params) {
return JobService.this.onStartJob(params);
}
@Override
public boolean onStopJob(JobParameters params) {
return JobService.this.onStopJob(params);
}
};
}
//返回JobServiceEngine对象中的binder对象,这也是在JobServiceContext中通过Binder的方式启动JobService获取到的Binder对象,因此当调用service的onStartJob方法时会调用到JobServiceEngine中binder对象的onStartJob方法,而在binder中的onStartJob方法则会通过Handler的方式进行处理
return mEngine.getBinder();
}
5、JobService返回值处理
在JobServiceContext中首先会调用到JobServiceEngine中Binder实现类的start方法,在该方法中会通过Handler的方式调用到其抽象函数onStartJob,而该函数在JobService中实现并调用JobService中的onStartJob函数,最后调用到自定义的JobService中。Handle部分源码如下:
final private class JobHandler extends Handler {
@Override
public void handleMessage(Message message) {
synchronized (mLock) {
if (!mReadyToRock) {
return;
}
switch (message.what) {
case MSG_JOB_EXPIRED: {
JobStatus runNow = (JobStatus) message.obj;
if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
mJobPackageTracker.notePending(runNow);
addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
} else {
queueReadyJobsForExecutionLocked();
}
} break;
//检查当前Job哪些满足执行条件
case MSG_CHECK_JOB:
//如果当前设备处于可交互状态则执行所有已经准备好的Job
if (mReportedActive) {
//在该函数中会停止执行那些不满足执行条件的Job,然后会将满足执行条件的Job按照进入的时间进行排序,并添加到待执行队列mPendingJobs中
queueReadyJobsForExecutionLocked();
} else {
//检查列表中的Job,当列表中满足可以执行条件的Job到达一定数量的时候则执行这些满足条件的Job
maybeQueueReadyJobsForExecutionLocked();
}
break;
case MSG_CHECK_JOB_GREEDY:
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null,
"app no longer allowed to run");
break;
case MSG_UID_STATE_CHANGED: {
final int uid = message.arg1;
final int procState = message.arg2;
updateUidState(uid, procState);
break;
}
case MSG_UID_GONE: {
final int uid = message.arg1;
final boolean disabled = message.arg2 != 0;
updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (disabled) {
cancelJobsForUid(uid, "uid gone");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
}
break;
}
case MSG_UID_ACTIVE: {
final int uid = message.arg1;
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, true);
}
break;
}
case MSG_UID_IDLE: {
final int uid = message.arg1;
final boolean disabled = message.arg2 != 0;
if (disabled) {
cancelJobsForUid(uid, "app uid idle");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
}
break;
}
}
maybeRunPendingJobsLocked();
removeMessages(MSG_CHECK_JOB);
}
}
}
经过一系列的调用最后会调用到handleStartedLocked方法中,在该方法中会对自定的JobService中的onStarJob中的返回值进行判断,源码如下:
/**
* State behaviours.
* VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
* _PENDING -> Error
* _EXECUTING -> Error
* _STOPPING -> Error
*/
private void handleStartedLocked(boolean workOngoing) {
switch (mVerb) {
case VERB_STARTING:
mVerb = VERB_EXECUTING;
//如果返回false则直接结束任务
if (!workOngoing) {
// Job已经执行完成因此结束任务,最后会调用到JobSchedulerService中的onJobCompletedLocked方法中
handleFinishedLocked(false, "onStartJob returned false");
return;
}
//判断是否已经取消执行Job,只有在onStartJob返回true的情况下才会被执行
if (mCancelled) {
...............
handleCancelLocked(null);
return;
}
scheduleOpTimeOutLocked();
break;
default:
..................
return;
}
}
private void handleCancelLocked(String reason) {
............
switch (mVerb) {
//调用自定义JobService中onStartJob方法没有完成或者还没有调用就取消执行任务
case VERB_BINDING:
case VERB_STARTING:
mCancelled = true;
applyStoppedReasonLocked(reason);
break;
case VERB_EXECUTING:
//如果onStartJob返回true时取消执行任务则执行结束任务(执行耗时任务)
sendStopMessageLocked(reason);
break;
case VERB_STOPPING:
break;
default:
Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
break;
}
}
private void applyStoppedReasonLocked(String reason) {
if (reason != null && mStoppedReason == null) {
mStoppedReason = reason;
mStoppedTime = SystemClock.elapsedRealtime();
if (mRunningCallback != null) {
mRunningCallback.mStoppedReason = mStoppedReason;
mRunningCallback.mStoppedTime = mStoppedTime;
}
}
}
private void sendStopMessageLocked(String reason) {
removeOpTimeOutLocked();
if (mVerb != VERB_EXECUTING) {
Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
closeAndCleanupJobLocked(false /* reschedule */, reason);
return;
}
try {
mVerb = VERB_STOPPING;
scheduleOpTimeOutLocked();
//最后会调用到自定义JobService中的onStopJob中,过程和调用onStartJob类似
service.stopJob(mParams);
} catch (RemoteException e) {
.....................
closeAndCleanupJobLocked(true /* reschedule */, "host crashed when trying to stop");
}
}
handleFinishedLocked会调用到closeAndCleanupJobLocked方法中最后调用到JobSchedulerService中的onJobCompletedLocked方法中,在closeAndCleanupJobLocked方法中也只是对变量重新做了初始化而已,因此直接看onJobCompletedLocked相关源码,如下:
public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
..............
//判断当前Job是否需要重新执行(不可避免的原因导致任务执行失败)
final JobStatus rescheduledJob = needsReschedule
? getRescheduleJobForFailureLocked(jobStatus) : null;
//isPeriodic任务是否允许循环执行
if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
// We still want to check for jobs to execute, because this job may have
// scheduled a new job under the same job id, and now we can run it.
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
return;
}
//如果一个job需要重新被执行则重新执行该job
if (rescheduledJob != null) {
try {
rescheduledJob.prepareLocked(ActivityManager.getService());
} catch (SecurityException e) {
Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
}
startTrackingJobLocked(rescheduledJob, jobStatus);
//如果该任务可以循环执行(定期)则给该任务设置执行时间并生成新的JobStatus
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
try {
rescheduledPeriodic.prepareLocked(ActivityManager.getService());
} catch (SecurityException e) {
Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
}
startTrackingJobLocked(rescheduledPeriodic, jobStatus);
}
jobStatus.unprepareLocked(ActivityManager.getService());
reportActiveLocked();
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean writeBack) {
//将旧任务上面的work转移到新的Job上面来
jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
// 从JobStore中移除旧的任务
final boolean removed = mJobs.remove(jobStatus, writeBack);
//从controller的集合中移除旧的任务
if (removed && mReadyToRock) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
}
}
return removed;
}
public boolean remove(JobStatus jobStatus, boolean writeBack) {
boolean removed = mJobSet.remove(jobStatus);
if (!removed) {
............
return false;
}
//如果被移除的任务不是一个可循环执行的任务但是在开机启动之后可以被执行
//则将所有Job存储到xml文件中
if (writeBack && jobStatus.isPersisted()) {
maybeWriteStatusToDiskAsync();
}
return removed;
}
6、总结
看到这里总算是把Job的创建、执行、返回值处理等过程分析的差不多了,当然里面肯定还有很多细节部分需要去深究才行。这个时候我们去分析一下为什么onStopJob中的返回值作用。首先考虑一下当上层调用了cancelJob方法会出现在什么情况下:
(1)步骤1:在执行完onStartJob之前或者Binder完成之前调用了cancelJob方法,此时JobServiceContext中mVerb=VERB_STARTING||VERB_BINDING,看源码发现会执行到applyStoppedReasonLocked方法中将mCancelled置为true并记录停止任务执行的时间。
步骤2:当上层onStartJob成功返回之后会执行到handleStartedLocked方法中,如果返回值为false则直接结束,如果返回值为true则会判断mCancelled是否为true,如果为true就会调用到sendStopMessageLocked方法中并将mVerb置为VERB_STOPPING同时调用onStopJob方法; 步骤3:最后onStopJob方法会调用到closeAndCleanupJobLocked方法中,并首先判断mVerb是否等于VERB_STOPPING,如果等于VERB_STOPPING则会直接return,整个过程结束,否则会执行onJobCompletedLocked方法;但是,很明显mVerb==VERB_STOPPING;
(2)当onStartJob返回true之后,并将mVerb置为VERB_EXECUTING,继续执行cancel job流程,会执行到sendStopMessageLocked方法中,并将mVerb置为VERB_STOPPING,然后执行onStopJob方法,然后执行步骤3。
经过如上分析不难得出不论onStopJob返回值是什么都不会有任何效果。具体的时序图如下所示。
文章中有不正之处还请提出。下面给出job开始执行时序图、cancelJob时序图以及调用jobFinished时序图:
6.1 job开执行始时序图

6.2 cancelJob结束时序图

6.3 jobFinished时序图
