文中的源代码版本为api23java
经过前面的学习,咱们知道若是Job
须要进行重试,那么会在JobSchedulerService.onJobCompleted
方法中生成一个新的JobStatus
实例,而后从新执行任务。 接下来咱们就来探讨一下api
Job
会进行重试首先咱们来看一下onJobCompleted
方法异步
public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
//log...
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();
}
复制代码
经过源码的阅读,咱们能够看到有两种重试场景ide
JobInfo.isPeriodic
返回true,Job
会周期性的反复执行needsReschedule
参数为true 咱们姑且称这种为失败重试定时任务是经过构造JobInfo
时调用Builder.setPeriodic
方法时设置的学习
/** * intervalMillis为时间间隔 */
public Builder setPeriodic(long intervalMillis) {
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
mHasEarlyConstraint = mHasLateConstraint = true;
return this;
}
复制代码
onJobCompleted
方法中,检测到Job
是定时任务的话,会经过getRescheduleJobForPeriodic
从新构造一个JobStatus
实例ui
private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
final long elapsedNow = SystemClock.elapsedRealtime();
// Compute how much of the period is remaining.
long runEarly = 0L;
// If this periodic was rescheduled it won't have a deadline.
if (periodicToReschedule.hasDeadlineConstraint()) {
//若是当前时间尚未到上个Job的deadLine,那么runEarly就是deadLine与当前时间的差
//不然runEarly为0
runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
}
//新的最先执行时间是当前时间+runEarly
long newEarliestRunTimeElapsed = elapsedNow + runEarly;
long period = periodicToReschedule.getJob().getIntervalMillis();
//新的deadLine为newEarliestRunTimeElapsed+intervalMillis
long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
//...
return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
newLatestRuntimeElapsed, 0 /* backoffAttempt */);
}
复制代码
新建立的JobStatus
对象相对于老对象主要更新了一下最先执行时间和最晚执行时间(deadLine
)。同时为了确保每一个时间间隔(intervalMillis
)至多只执行一次,最先执行时间还会加上一个runEarly
.this
失败重试依赖于onJobCompleted
的调用点所传的reschedule
参数。 onJobCompleted
的调用点只有一个JobServiceHandler.closeAndCleanupJobH
,然后者的调用点则有多个,其中reschedule
参数为true
的调用点4个。spa
第一个调用点发生在JobServiceHandler.handleMessage
的MSG_SHUTDOWN_EXECUTION
case中。 而触发MSG_SHUTDOWN_EXECUTION
消息的地方有两个code
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
JobStatus runningJob;
synchronized (mLock) {
//...
runningJob = mRunningJob;
}
if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
//第一个点
mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
return;
}
//...
}
/** If the client service crashes we reschedule this job and clean up. */
@Override
public void onServiceDisconnected(ComponentName name) {
//第二个点
mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
}
复制代码
因此总结以下对象
mRunningJob
为空,或者服务与JobStatus
中指定的组件不一致则发送MSG_SHUTDOWN_EXECUTION
消息MSG_SHUTDOWN_EXECUTION
消息private void handleServiceBoundH() {
//...
if (mCancelled.get()) {
//log...
closeAndCleanupJobH(true /* reschedule */);
return;
}
//...
}
复制代码
handleServiceBoundH
方法的触发时机在服务绑定成功以后,onStartJob
触发以前,这段时间内若是Job
被取消,则会重试。
private void handleFinishedH(boolean reschedule) {
switch (mVerb) {
case VERB_EXECUTING:
case VERB_STOPPING:
closeAndCleanupJobH(reschedule);
break;
default:
//log...
}
}
复制代码
handleFinishedH
的调用点有两个,其中一个调用点入参写死为false
,因此咱们只须要看另外一个就行了。另外一个调用点在JobServiceHandler.handleMessage
的MSG_CALLBACK
case中。
public void handleMessage(Message message) {
switch (message.what) {
//...
case MSG_CALLBACK:
//...
if (mVerb == VERB_STARTING) {
//...
} else if (mVerb == VERB_EXECUTING ||
mVerb == VERB_STOPPING) {
final boolean reschedule = message.arg2 == 1;
handleFinishedH(reschedule);
} else {
///...
}
break;
//...
}
}
复制代码
mVerb
为VERB_EXECUTING
、VERB_STOPPING
的场景分别对应Job
异步执行完毕的回调(jobFinished
)以及执行完onStopJob
后的回调。 而reshedule
则对应着jobFinished
方法的第二个入参以及onStopJob
的返回值。
第四个调用点在超时处理的VERB_STOPPING
case中
private void handleOpTimeoutH() {
switch (mVerb) {
//...
case VERB_STOPPING:
//log...
closeAndCleanupJobH(true /* needsReschedule */);
break;
//...
}
}
复制代码
也就是说若是onStopJob
方法超时,也会重试。
失败重试的任务是经过getRescheduleJobForFailure
方法生成的
private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
final long elapsedNowMillis = SystemClock.elapsedRealtime();
final JobInfo job = failureToReschedule.getJob();
final long initialBackoffMillis = job.getInitialBackoffMillis();
final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
long delayMillis;
switch (job.getBackoffPolicy()) {
case JobInfo.BACKOFF_POLICY_LINEAR:
delayMillis = initialBackoffMillis * backoffAttempts;
break;
default:
if (DEBUG) {
Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
}
case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
delayMillis =
(long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
break;
}
delayMillis =
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
return new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
}
复制代码
该方法会根据back-off
策略,从新设置Job
的延迟时间,同时将back-off
次数加1,还有一个须要注意的是失败重试的任务没有deadLine
。
综上所述,咱们能够整理出如下重试场景
jobFinished
第二个入参(needsReschedule
)传true
onStopJob
返回true
onStopJob
方法超时onStartJob
触发以前被取消mRunningJob
字段为空,或者启动的服务与mRunningJob
指定的不一致