// 构建JobInfo对象,传递给JobSchedulerService
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(mContext, AliveJobService.class));
builder.setPeriodic(5000);
builder.setPersisted(true);
builder.setRequiresCharging(true);
JobInfo info = builder.build();
mJobScheduler.schedule(info);
这段定时任务在每隔5秒执行一次任务,Android 5.0和6.0系统能够正常运行.但是在Android7.0不能正常工作了。
https://stackoverflow.com/questions/39641278/job-scheduler-in-android-n-with-less-then-15-minutes-interval?rq=1
https://stackoverflow.com/questions/38344220/job-scheduler-not-running-on-android-n/38774104
看万两片关于JobService 7.0(Nougat) 看完有几个疑问
1.
如果想到在小于15分钟间隔执行为什么要设置
setMinimumLatency()?
2.setBackoffCriteria(youtime, JobInfo.BACKOFF_POLICY_LINEAR)这个是有什么作用,如果不设置会怎么样?
3.
如果设置
obFinished
(
parameters
,
true
)
和
obFinished
(
parameters
,
false
)
有什么区别
?
4.如果想重复执行怎么操作?
想知道这些问题的答案肯定要看源码
andorid 7.5JobInfo源码
package android.app.job;
public class JobInfo implements Parcelable {
//...
* Amount of backoff a job has initially by default, in milliseconds.
public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 30 seconds.
* Maximum backoff we allow for a job, in milliseconds.
public static final long MAX_BACKOFF_DELAY_MILLIS = 5 * 60 * 60 * 1000; // 5 hours.
public static final int BACKOFF_POLICY_LINEAR = 0;
public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
//默认指数
public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
/* Minimum interval for a periodic job, in milliseconds. */
private static final long MIN_PERIOD_MILLIS = 15 * 60 * 1000L; // 15 minutes
/* Minimum flex for a periodic job, in milliseconds. */
private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
* Specify that this job should be delayed by the provided amount of time.
* Because it doesn't make sense setting this property on a periodic job, doing so will
* throw an {@link java.lang.IllegalArgumentException} when
* {@link android.app.job.JobInfo.Builder#build()} is called.
* @param minLatencyMillis Milliseconds before which this job will not be considered for
* execution.
public Builder setMinimumLatency(long minLatencyMillis) {
mMinLatencyMillis = minLatencyMillis;
mHasEarlyConstraint = true;
return this;
* 最小15分钟
public static final long getMinPeriodMillis() {
return MIN_PERIOD_MILLIS;
public Builder setPeriodic(long intervalMillis, long flexMillis) {
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
mFlexMillis = flexMillis;
mHasEarlyConstraint = mHasLateConstraint = true;
return this;
* 最小15分钟 如果值比15分钟大取大的值
public long getIntervalMillis() {
return intervalMillis >= getMinPeriodMillis() ? intervalMillis : getMinPeriodMillis();
* 最小5分钟
public static final long getMinFlexMillis() {
return MIN_FLEX_MILLIS;
* Math.max(percentClamp, getMinFlexMillis())最小15分钟
* flexMillis 是setPeriodic(long intervalMillis, long flexMillis)第二参数,默认这个和intervalMillis
* clampedFlex的值最小值15分钟
public long getFlexMillis() {
long interval = getIntervalMillis();//最小15分钟
long percentClamp = 5 * interval / 100; //取时间间隔的5%
//先取getMinFlexMillis()五分钟 和getIntervalMillis()的5%的取最大值
//然后和设置的setPeriodic(long intervalMillis, long flexMillis)中的flexMillis取最大值
//所有这里最小的值15分钟
long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinFlexMillis()));
//如果这个值比间隔小,去这个值,如果比间隔大去间隔值
return clampedFlex <= interval ? clampedFlex : interval;
* The amount of time the JobScheduler will wait before rescheduling a failed job. This value
* will be increased depending on the backoff policy specified at job creation time. Defaults
* to 5 seconds.
public long getInitialBackoffMillis() {
return initialBackoffMillis;
public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
mBackoffPolicySet = true;
mInitialBackoffMillis = initialBackoffMillis;
mBackoffPolicy = backoffPolicy;
return this;
//...
JobInfo这里
看完这个类可以回答这个问题
1.如果想到在小于15分钟间隔执行为什么要设置.setMinimumLatency()
在7.0调用setPeriodic()之后在获取间隔时间getIntervalMillis() 强制使用了最小时间15分钟。所以想通过setPeriodic()来设置小于15分钟间隔是不行的。所以如果小于15分钟需要通过设置setMinimumLatency ()
public Builder setMinimumLatency(long minLatencyMillis) {
mMinLatencyMillis = minLatencyMillis;
mHasEarlyConstraint = true;
return this;
* 最小15分钟
public static final long getMinPeriodMillis() {
return MIN_PERIOD_MILLIS;
public Builder setPeriodic(long intervalMillis, long flexMillis) {
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
mFlexMillis = flexMillis;
mHasEarlyConstraint = mHasLateConstraint = true;
return this;
* 最小15分钟 如果值比15分钟大取大的值
public long getIntervalMillis() {
return intervalMillis >= getMinPeriodMillis() ? intervalMillis : getMinPeriodMillis();
1.getIntervalMillis()最小值15分钟
2.getFlexMillis()最小值15分钟
3.getMinFlexMillis最小值5分钟
andorid 7.1NJobSchedulerService源码
public final class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {
//...
862 /**
863 * Called when we have a job status object that we need to insert in our
864 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
865 * about.
866 */
867 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
868 synchronized (mLock) {
869 final boolean update = mJobs.add(jobStatus);
870 if (mReadyToRock) {
871 for (int i = 0; i < mControllers.size(); i++) {
872 StateController controller = mControllers.get(i);
873 if (update) {
874 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
875 }
876 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
877 }
878 }
879 }
880 }
882 /**
883 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
884 * object removed.
先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件,通知控制器,取消 track!
885 */
886 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
887 boolean writeBack) {
888 synchronized (mLock) {
889 // Remove from store as well as controllers. 先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件!
890 final boolean removed = mJobs.remove(jobStatus, writeBack);
891 if (removed && mReadyToRock) {
892 for (int i=0; i<mControllers.size(); i++) {
893 StateController controller = mControllers.get(i);//通知控制器,取消 track!
894 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
895 }
896 }
897 return removed;
898 }
899 }
1026 /**
1027 * A job just finished executing. We fetch the
1028 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1029 * whether we want to reschedule we readd it to the controllers.
1030 * @param jobStatus Completed job.
1031 * @param needsReschedule Whether the implementing class should reschedule this job.
1032 */
1033 @Override
1034 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
1035 if (DEBUG) {
1036 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1037 }
1038 // Do not write back immediately if this is a periodic job. The job may get lost if system
1039 // shuts down before it is added back.
1040 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {//调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除:
1041 if (DEBUG) {
1042 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1043 }
1044 // We still want to check for jobs to execute, because this job may have
1045 // scheduled a new job under the same job id, and now we can run it.
1046 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1047 return;
1048 }
1049 // Note: there is a small window of time in here where, when rescheduling a job,
1050 // we will stop monitoring its content providers. This should be fixed by stopping
1051 // the old job after scheduling the new one, but since we have no lock held here
1052 // that may cause ordering problems if the app removes jobStatus while in here.
1053 if (needsReschedule) {
1054 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
1055 startTrackingJob(rescheduled, jobStatus);
1056 } else if (jobStatus.getJob().isPeriodic()) {//如果JobInfo调用setPeriodic会设置true
1057 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1058 startTrackingJob(rescheduledPeriodic, jobStatus);
1059 }
1060 reportActive();
1061 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();//发送 MSG_CHECK_JOB_GREEDY 给 JobSchedulerService.JobHandler
1062 }
947 /**
948 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
949 * specify an override deadline on a failed job (the failed job will run even though it's not
950 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
951 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
952 *
953 * @param failureToReschedule Provided job status that we will reschedule.
954 * @return A newly instantiated JobStatus with the same constraints as the last job except
955 * with adjusted timing constraints.
956 *
957 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
958 */
959 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
960 final long elapsedNowMillis = SystemClock.elapsedRealtime();
961 final JobInfo job = failureToReschedule.getJob();
963 final long initialBackoffMillis = job.getInitialBackoffMillis();
964 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
965 long delayMillis;
967 switch (job.getBackoffPolicy()) {
968 case JobInfo.BACKOFF_POLICY_LINEAR:
969 delayMillis = initialBackoffMillis * backoffAttempts;
970 break;
971 default:
972 if (DEBUG) {
973 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
974 }
975 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
976 delayMillis =
977 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
978 break;
979 }
980 delayMillis =
981 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);//和5小时比较
982 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
983 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);//构建新的JobStatu 设置下次时间 设置backoff次数
984 for (int ic=0; ic<mControllers.size(); ic++) {
985 StateController controller = mControllers.get(ic);
986 controller.rescheduleForFailure(newJob, failureToReschedule);
987 }
988 return newJob;
989 }
991 /**
992 * Called after a periodic has executed so we can reschedule it. We take the last execution
993 * time of the job to be the time of completion (i.e. the time at which this function is
994 * called).
995 * This could be inaccurate b/c the job can run for as long as
996 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
997 * to underscheduling at least, rather than if we had taken the last execution time to be the
998 * start of the execution.
999 * @return A new job representing the execution criteria for this instantiation of the
1000 * recurring job.
1001 */
1002 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1003 final long elapsedNow = SystemClock.elapsedRealtime();
1004 // Compute how much of the period is remaining.
1005 long runEarly = 0L;
1007 // If this periodic was rescheduled it won't have a deadline.
1008 if (periodicToReschedule.hasDeadlineConstraint()) {
1009 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1010 }
1011 long flex = periodicToReschedule.getJob().getFlexMillis();
1012 long period = periodicToReschedule.getJob().getIntervalMillis();//间隔时间
1013 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1014 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1016 if (DEBUG) {
1017 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1018 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1019 }
1020 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1021 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1022 }
//...
3.如果设置obFinished(parameters, true)和obFinished(parameters, false)有什么区别?
这里关键在onJobCompleted
首先调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除,因为之前已经移除过了,所以这个 stopTrackingJob 的返回值为 false
@Override
public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
// Do not write back immediately if this is a periodic job. The job may get lost if system
// shuts down before it is added back.
// 再次停止 track 这个 job,这里 stopTrackingJob 的返回值为 false!
if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
// We still want to check for jobs to execute, because this job may have
// scheduled a new job under the same job id, and now we can run it.
// 发送 MSG_CHECK_JOB_GREEDY,继续执行其他的 job,然后直接 return
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
return;
// Note: there is a small window of time in here where, when rescheduling a job,
// we will stop monitoring its content providers. This should be fixed by stopping
// the old job after scheduling the new one, but since we have no lock held here
// that may cause ordering problems if the app removes jobStatus while in here.
if (needsReschedule) {
JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
startTrackingJob(rescheduled, jobStatus);
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
startTrackingJob(rescheduledPeriodic, jobStatus);
reportActive();
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
如果是true那么needsReschedule的也是true执行就是这段代码
if (needsReschedule) {
JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
startTrackingJob(rescheduled, jobStatus);
这段点主要调用getRescheduleJobForFailure
private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
final long elapsedNowMillis = SystemClock.elapsedRealtime();
final JobInfo job = failureToReschedule.getJob();
final long initialBackoffMillis = job.getInitialBackoffMillis();//获取setBackoffCriteria设置的值,如果没有设置默认是30秒
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;
//getNumFailures值越大这个值也越大间隔时间会越来越长
delayMillis =
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);//和5小时比较
JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, backoffAttempts);//构建新的JobStatu 设置下次时间 设置backoff次数
for (int ic=0; ic<mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailure(newJob, failureToReschedule);
return newJob;
看完这段代码可以回答
2.setBackoffCriteria(youtime, JobInfo.BACKOFF_POLICY_LINEAR)这个是有什么作用,如果不设置会怎么样?
这个代码可以看出如果不想默认的时间是30秒,默认指数DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL
就必须通过setBackoffCriteria()来设置时间 和线性或者指数增长策略
如果是false 那么needsReschedule的也是false 执行就是这段代码
这段如果设置了setPeriodic()被调用那么isPeriodic()是true则下面代码被执行
else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
startTrackingJob(rescheduledPeriodic, jobStatus);
这段代码主要调用getRescheduleJobForPeriodic方法
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()) {
runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
long flex = periodicToReschedule.getJob().getFlexMillis();//最小15分钟
long period = periodicToReschedule.getJob().getIntervalMillis();//最小15分钟
long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
newLatestRuntimeElapsed, 0 /* backoffAttempt */);
//...
4.如果想重复执行怎么操作?
1.设置jobFinished((JobParameters) msg.obj, true);设置true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
jobFinished((JobParameters) msg.obj, true);
} else {
jobFinished((JobParameters) msg.obj, false);
2.在jobFinished在jobFinished之前重新调用startJobScheduler
以下是android7.0怎么设置startJobScheduler执行的
public void startJobScheduler() {
if (DEBUG) {
Log.i(TAG, "startJobScheduler");
int id = JOB_ID;
if (DEBUG) {
Log.i(TAG, "开启AstJobService id=" + id);
mJobScheduler.cancel(id);
JobInfo.Builder builder = new JobInfo.Builder(id, new ComponentName(mContext, AstJobService.class));
if (Build.VERSION.SDK_INT >= 24) {
builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最小延迟时间
builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最长延时时间
builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
} else {
builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
builder.setPersisted(true); // 设置设备重启时,执行该任务
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setRequiresCharging(true); // 当插入充电器,执行该任务
JobInfo info = builder.build();
mJobScheduler.schedule(info); //开始定时执行该系统任务