文中的源代码版本为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
的销毁流程了。