文中的源代码版本为api23
JobScheduler工作流程
接下来我们从源码角度去深入理解JobScheduler的运行机制。
客户端调用JobScheduler.schedule方法之后,通过Binder通信会进入到JobSchedulerStub.schedule方法
1 JobScheduler工作流程
1.1 JobSchedulerStub.schedule方法
final class JobSchedulerStub extends IJobScheduler.Stub {
public int schedule(JobInfo job) throws RemoteException {
//log..
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
//主要是校验一下Service是否存在,是否拥有BIND_JOB_SERVICE权限
enforceValidJobRequest(uid, job);
if (job.isPersisted()) {
//如果Job需要持久化,那需要校验是否有RECEIVE_BOOT_COMPLETED权限
if (!canPersistJobs(pid, uid)) {
throw new IllegalArgumentException("Error: requested job be persisted without"
+ " holding RECEIVE_BOOT_COMPLETED permission.");
}
}
long ident = Binder.clearCallingIdentity();
try {
//JobSchedulerStub是JobSchedulerService的内部类,后续流程
//直接抛给JobSchedulerService来完成
return JobSchedulerService.this.schedule(job, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
JobSchedulerStub.schedule方法的逻辑比较简单,主要干了两件事:
- 做一些权限校验
- 调用
JobSchedulerService.schedule方法继续后续流程
1.2 JobSchedulerService.schedule方法
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
该方法的逻辑还是非常清晰的:
- 封装
JobInfo为JobStatus - 取消具有相同jobId的老任务
3 调用
startTrackingJob开始进行状态跟踪(重点) - 发送一个
MSG_CHECK_JOB消息
其中2、4步我们放到后面讲,先关注核心方法startTrackingJob
1.3 JobSchedulerService.startTrackingJob方法
private void startTrackingJob(JobStatus jobStatus) {
boolean update;
boolean rocking;
synchronized (mJobs) {
//mJobs是Job的管理者,类型为JobStore
//mJobs内部使用ArraySet来保存JobStatus
//JobStore.add方法涉及到ArraySet的remove和add操作
//update就是ArraySet.remove方法的返回值
//由于当前的JobStatus是全新的
//因此此处的update为false
update = mJobs.add(jobStatus);
//mReadyToRock字段用来跟踪系统启动状态
//一般情况下mReadyToRock都为true
rocking = mReadyToRock;
}
if (rocking) {
for (int i=0; i<mControllers.size(); i++) {
//StateController
StateController controller = mControllers.get(i);
//...
controller.maybeStartTrackingJob(jobStatus);
}
}
}
该方法的核心逻辑是遍历所有的StateController并执行其maybeStartTrackingJob方法。
JobSchedulerService使用一个名为mControllers的变量保存StateController,其类型为List,在JobSchedulerService的构造函数中被初始化。
public JobSchedulerService(Context context) {
super(context);
// Create the controllers.
mControllers = new ArrayList<StateController>();
mControllers.add(ConnectivityController.get(this));
mControllers.add(TimeController.get(this));
mControllers.add(IdleController.get(this));
mControllers.add(BatteryController.get(this));
mControllers.add(AppIdleController.get(this));
//...
}
可以看到,StateController的派生类有很多,有ConnectivityController、TimeController、IdleController、BatteryController、AppIdleController,JobSchedulerService正是通过这些StateController实现了对网络连接状态、时间、设备空闲状态、电池电量、应用空闲状态等的监听。
为了便于分析,我们以相对简单的AppIdleController为例,继续流程分析。
1.4 AppIdleController.maybeStartTrackingJob方法
public void maybeStartTrackingJob(JobStatus jobStatus) {
synchronized (mTrackedTasks) {
//mTrackedTasks为ArrayList<JobStatus>类型
//AppIdleController使用该字段保存JobStatus
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.job.getService().getPackageName();
//通过UsageStatsService获取当前应用是否处于空闲状态
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
jobStatus.uId, jobStatus.getUserId());
//debug log...
//根据当前应用状态设置JobStatus的对应字段
//appNotIdleConstraintSatisfied为AtomicBoolean类型
//用来记录当前app的空闲状态
jobStatus.appNotIdleConstraintSatisfied.set(!appIdle);
}
}
maybeStartTrackingJob逻辑比较简单,干了两件事情
- 保存
JobStatus - 根据当前应用状态设置
JobStatus.appNotIdleConstraintSatisfied字段的值
那么流程到这里就断了么?显然不会,AppIdleController内部会一直跟踪应用状态,当应用状态发生变化时会通知JobSchedulerService。
我们先来看看AppIdleController的构造函数
private AppIdleController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
}
构造函数的逻辑非常简单,首先是获取UsageStatsService服务,然后注册了一个应用状态变更监听。
AppIdleStateChangeListener是AppIdleController的一个内部类,当应用状态发生变化时会回调其onAppIdleStateChanged方法,我们直接上代码
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
boolean changed = false;
synchronized (mTrackedTasks) {
//这个return逻辑先不管它
if (mAppIdleParoleOn) {
return;
}
for (JobStatus task : mTrackedTasks) {
if (task.job.getService().getPackageName().equals(packageName)
&& task.getUserId() == userId) {
if (task.appNotIdleConstraintSatisfied.get() != !idle) {
//debug log...
//应用状态发生改变,更新对应的字段
task.appNotIdleConstraintSatisfied.set(!idle);
changed = true;
}
}
}
}
if (changed) {
//只要有Job的状态发生了变化就会触发回调
mStateChangedListener.onControllerStateChanged();
}
}
逻辑非常简单,就是遍历所有的JobStatus看状态是否发生变化,如果是,则更新appNotIdleConstraintSatisfied字段。同时,只要有一个JobStatus的状态被更新,就会触发一个回调。
mStateChangedListener的实现类就是JobSchedulerService,由于只有一句代码,就不贴出来了。该方法就是通过JobHandler(JobSchedulerService的一个内部类,派生自Handler)发送了一个MSG_CHECK_JOB消息,接着就会依次触发maybeQueueReadyJobsForExecutionLockedH和maybeRunPendingJobsH。
1.5 JobHandler.maybeQueueReadyJobsForExecutionLockedH方法
private void maybeQueueReadyJobsForExecutionLockedH() {
int chargingCount = 0;
int idleCount = 0;
int backoffCount = 0;
int connectivityCount = 0;
List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
//通过JobStore获取当前所有的Job
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
//使用isReadyToBeExecutedLocked方法判断
//当前的Job所以来的约束条件是否已经满足
if (isReadyToBeExecutedLocked(job)) {
if (job.getNumFailures() > 0) {
//计算重试的Job数量
backoffCount++;
}
if (job.hasIdleConstraint()) {
//计算依赖设备空闲状态的Job数量
idleCount++;
}
if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
//计算依赖网络类型的Job数量
connectivityCount++;
}
if (job.hasChargingConstraint()) {
//计算依赖充电状态的Job数量
chargingCount++;
}
runnableJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
//Job的stop流程,先不管它
stopJobOnServiceContextLocked(job);
}
}
//特殊机制
if (backoffCount > 0 ||
idleCount >= MIN_IDLE_COUNT ||
connectivityCount >= MIN_CONNECTIVITY_COUNT ||
chargingCount >= MIN_CHARGING_COUNT ||
runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
//debug log...
for (int i=0; i<runnableJobs.size(); i++) {
mPendingJobs.add(runnableJobs.get(i));
}
} else {
//debug log...
}
//debug log...
}
maybeQueueReadyJobsForExecutionLockedH方法会遍历当前列表中的所有JobStatus寻找已经满足执行条件的添加到runnableJobs列表中。
并不是满足了执行条件就会执行,JobSchedulerService有一个机制,它会以约束条件为维度进行计数,并为各个约束条件设置了一个阈值,只有超过阈值的Job才会添加到待执行列表mPendingJobs中。
1.6 JobHandler.maybeRunPendingJobsH方法
private void maybeRunPendingJobsH() {
synchronized (mJobs) {
if (mDeviceIdleMode) {
//设备空闲状态禁止执行任何任务
return;
}
Iterator<JobStatus> it = mPendingJobs.iterator();
//debug log...
while (it.hasNext()) {
JobStatus nextPending = it.next();
JobServiceContext availableContext = null;
//这个for循环的工作时寻找可用的JobServiceContext
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus running = jsc.getRunningJob();
if (running != null && running.matches(nextPending.getUid(),
nextPending.getJobId())) {
// Job已经在运行,则跳过
availableContext = null;
break;
}
if (jsc.isAvailable()) {
//找到了可用的JobServiceContext
availableContext = jsc;
}
}
if (availableContext != null) {
//debug log...
//调用JobServiceContext.executeRunnableJob方法执行任务
if (!availableContext.executeRunnableJob(nextPending)) {
//debug log...
//JobServiceContext执行任务失败,则将Job从JobStore中移除
mJobs.remove(nextPending);
}
//从mPendingJobs中移除对应的JobStatus
it.remove();
}
}
}
}
该方法的主要逻辑很简单:为所有待启动任务寻找到可用的JobServiceContext,并执行任务(通过JobServiceContext.executeRunnableJob方法)。
那么JobServiceContext又是什么呢?我们知道一个Job就相当于一个Service,而JobServiceContext则负责与Service的对接工作。
1.7 JobServiceContext.enecuteRunnableJob
boolean executeRunnableJob(JobStatus job) {
synchronized (mLock) {
//...
mRunningJob = job;
final boolean isDeadlineExpired =
job.hasDeadlineConstraint() &&
(job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
//创建一个JobParameters
//JobParameters的第一个参数是一个IBinder对象
//后续JobService可以拿到该对象与JobServiceContext
//进行通信
mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired);
mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
mVerb = VERB_BINDING;
//发送超时消息
scheduleOpTimeOut();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
//创建并绑定服务
boolean binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
new UserHandle(job.getUserId()));
if (!binding) {
//绑定失败的一些清理工作
return false;
}
//...
mAvailable = false;
return true;
}
}
executeRunnableJob的主要工作就是通过bindServiceAsUser启动了JobService。由于JobServiceContext实现了ServiceConnection接口,后续JobServiceContext便可以与JobService进行通信。
有关服务绑定的细节我们就不赘述了,服务绑定成功之后会回调ServiceConnection.onServiceConnected方法。JobServiceContext.onServiceConnected方法的实现较简单,有兴趣大家可以自己分析,它会发送一个MSG_SERVICE_BOUND消息,最终触发JobServiceHandler.handleServiceBoundH方法(JobServiceHandler是JobServiceContext的内部类)。
1.8 JobServiceHandler.handleServiceBoundH方法
private void handleServiceBoundH() {
//debug log...
//异常检查
if (mVerb != VERB_BINDING) {
Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+ VERB_STRINGS[mVerb]);
closeAndCleanupJobH(false /* reschedule */);
return;
}
//是否已取消
if (mCancelled.get()) {
if (DEBUG) {
Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
+ mRunningJob);
}
closeAndCleanupJobH(true /* reschedule */);
return;
}
try {
mVerb = VERB_STARTING;
scheduleOpTimeOut();
//service便是JobService.onBind方法返回的Binder对象
//mParams是在executeRunnableJob方法中生成的
service.startJob(mParams);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending onStart message to '" +
mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
}
}
handleServiceBoundH的主要逻辑页很简单,就是调用了JobService返回的Binder对象的startJob方法。这样JobService.onStartJob方法便触发了。
至此,JobScheduler的工作流程已经基本结束了。
1.9 总结
JobScheduler的工作主要由JobSchedulerService、JobServiceContext以及各种StateController协同完成。
其中StateController负责监听设备的各种状态、更新JobStatus中对应字段的值,并通知JobSchedulerService.
JobSchedulerService收到StateController的消息后,就开始检测Job的状态,如果有满足执行条件的Job则交给JobServiceContext执行。
而JobServiceContext则负责绑定服务等于JobService生命周期相关的事宜。
2 JobService销毁流程
JobService的销毁流程在前半部分会有两个分支:
- 当
JobService的工作在onStartJob方法中就完成了,那么通过返回false就可以结束了(触发JobSchedulerContext.acknowledgeStartMessage方法)。(以下简称分支1) - 当
JobService执行的是一些耗时任务,那么则需要异步执行任务,那么就需要调用JobService.jobFinished方法来结束任务(触发JobSchedulerContext.jobFinished方法)。而此时onStartJob方法需要返回false。(以下简称分支2)
无论是acknowledgeStartMessage还是jobFinished,最终都会进入到JobServiceHandler.handleMessage方法的MSG_CALLBACK这个case中。
public void handleMessage(Message message) {
switch (message.what) {
//...
case MSG_CALLBACK:
//...
if (mVerb == VERB_STARTING) {
//acknowledgeStartMessage走这个分支
final boolean workOngoing = message.arg2 == 1;
handleStartedH(workOngoing);
} else if (mVerb == VERB_EXECUTING ||
mVerb == VERB_STOPPING) {
//jobFinished走这个分支
final boolean reschedule = message.arg2 == 1;
handleFinishedH(reschedule);
} else {
//...
}
break;
//...
}
}
从流程上来讲,分支1只会走handleStartedH方法,而分支2会依次走handleStartedH、handleFinishedH方法。
2.1 JobServiceHandler.handleStartedH方法
private void handleStartedH(boolean workOngoing) {
switch (mVerb) {
case VERB_STARTING:
mVerb = VERB_EXECUTING;
if (!workOngoing) {
//分支1场景workOngoing为false,立刻执行handleFinishedH
handleFinishedH(false);
return;
}
//取消流程
if (mCancelled.get()) {
//log...
handleCancelH();
return;
}
//分支2场景,workOngong为true,执行一个超时消息
//后续JobService执行完毕调用jobFinished方法
//还是会进入到handleFinishedH中。
scheduleOpTimeOut();
break;
default:
//log...
return;
}
}
2.2 JobServiceHandler.handleFinishedH
private void handleFinishedH(boolean reschedule) {
switch (mVerb) {
case VERB_EXECUTING:
case VERB_STOPPING:
closeAndCleanupJobH(reschedule);
break;
default:
//log...
}
}
直接调用了closeAndCleanupJobH方法
private void closeAndCleanupJobH(boolean reschedule) {
final JobStatus completedJob = mRunningJob;
synchronized (mLock) {
//...
mContext.unbindService(JobServiceContext.this);
mWakeLock = null;
mRunningJob = null;
mParams = null;
mVerb = -1;
mCancelled.set(false);
service = null;
mAvailable = true;
}
removeOpTimeOut();
removeMessages(MSG_CALLBACK);
removeMessages(MSG_SERVICE_BOUND);
removeMessages(MSG_CANCEL);
removeMessages(MSG_SHUTDOWN_EXECUTION);
mCompletedListener.onJobCompleted(completedJob, reschedule);
}
主要做了这些事情:
- 解绑服务
- 重置一些变量的状态
- 移除消息队列中的所有消息
- 执行一个成功回调
mCompletedListener的本体就是JobSchedulerService。
2.3 JobSchedulerService.onJobCompleted
public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
//log...
//stopTrackingJob的工作主要是讲JobStatus从JobStore
//和各种StateController中移除
//是与startTrackingJob相反的一个过程
if (!stopTrackingJob(jobStatus)) {
//log...
return;
}
if (needsReschedule) {
JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
startTrackingJob(rescheduled);
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
startTrackingJob(rescheduledPeriodic);
}
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
主要工作:
- 将
JobStatus从JobStore和各种StateController中移除 - 如果需要重新执行,那么会生成一个新的
JobStatus并调用startTrackingJob方法 - 不管是否要重新执行,都会发送
MSG_CHECK_JOB消息,检查是否有满足条件的Job能够被执行。
以上,就是JobService的销毁流程了。