文中的源代碼版本爲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
指定的不一致