文中的源代码版本为api23java
接下来咱们从源码角度去深刻理解JobScheduler
的运行机制。 客户端调用JobScheduler.schedule
方法以后,经过Binder
通讯会进入到JobSchedulerStub.schedule
方法api
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
方法继续后续流程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;
}
复制代码
该方法的逻辑仍是很是清晰的:app
JobInfo
为JobStatus
startTrackingJob
开始进行状态跟踪(重点)MSG_CHECK_JOB
消息其中二、4步咱们放到后面讲,先关注核心方法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
的构造函数中被初始化。ide
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
为例,继续流程分析。函数
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
逻辑比较简单,干了两件事情ui
JobStatus
JobStatus.appNotIdleConstraintSatisfied
字段的值那么流程到这里就断了么?显然不会,AppIdleController
内部会一直跟踪应用状态,当应用状态发生变化时会通知JobSchedulerService
。this
咱们先来看看AppIdleController
的构造函数spa
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
。
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
中。
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
的对接工做。
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
的内部类)。
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
的工做流程已经基本结束了。
JobScheduler
的工做主要由JobSchedulerService
、JobServiceContext
以及各类StateController
协同完成。 其中StateController
负责监听设备的各类状态、更新JobStatus
中对应字段的值,并通知JobSchedulerService
. JobSchedulerService
收到StateController
的消息后,就开始检测Job
的状态,若是有知足执行条件的Job
则交给JobServiceContext
执行。 而JobServiceContext
则负责绑定服务等于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
方法。
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;
}
}
复制代码
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
。
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
的销毁流程了。