代码
//构建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/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.如果想重复执行怎么操作?
想知道这些问题的答案肯定要看源码
packageandroid.app.job;public class JobInfo implementsParcelable {//...
/*** 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 {@linkjava.lang.IllegalArgumentException} when
* {@linkandroid.app.job.JobInfo.Builder#build()} is called.
*@paramminLatencyMillis Milliseconds before which this job will not be considered for
* execution.*/
public Builder setMinimumLatency(longminLatencyMillis) {
mMinLatencyMillis=minLatencyMillis;
mHasEarlyConstraint= true;return this;
}/*** 最小15分钟*/
public static final longgetMinPeriodMillis() {returnMIN_PERIOD_MILLIS;
}public Builder setPeriodic(long intervalMillis, longflexMillis) {
mIsPeriodic= true;
mIntervalMillis=intervalMillis;
mFlexMillis=flexMillis;
mHasEarlyConstraint= mHasLateConstraint = true;return this;
}/*** 最小15分钟 如果值比15分钟大取大的值*/
public longgetIntervalMillis() {return intervalMillis >= getMinPeriodMillis() ?intervalMillis : getMinPeriodMillis();
}/*** 最小5分钟*/
public static final longgetMinFlexMillis() {returnMIN_FLEX_MILLIS;
}/*** Math.max(percentClamp, getMinFlexMillis())最小15分钟
* flexMillis 是setPeriodic(long intervalMillis, long flexMillis)第二参数,默认这个和intervalMillis
* clampedFlex的值最小值15分钟*/
public longgetFlexMillis() {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 longgetInitialBackoffMillis() {returninitialBackoffMillis;
}public Builder setBackoffCriteria(long initialBackoffMillis, intbackoffPolicy) {
mBackoffPolicySet= true;
mInitialBackoffMillis=initialBackoffMillis;
mBackoffPolicy=backoffPolicy;return this;
}//...
}
JobInfo这里
看完这个类可以回答这个问题
1.如果想到在小于15分钟间隔执行为什么要设置.setMinimumLatency()
在7.0调用setPeriodic()之后在获取间隔时间getIntervalMillis() 强制使用了最小时间15分钟。所以想通过setPeriodic()来设置小于15分钟间隔是不行的。所以如果小于15分钟需要通过设置setMinimumLatency ()
public Builder setMinimumLatency(longminLatencyMillis) {
mMinLatencyMillis=minLatencyMillis;
mHasEarlyConstraint= true;return this;
}/*** 最小15分钟*/
public static final longgetMinPeriodMillis() {returnMIN_PERIOD_MILLIS;
}public Builder setPeriodic(long intervalMillis, longflexMillis) {
mIsPeriodic= true;
mIntervalMillis=intervalMillis;
mFlexMillis=flexMillis;
mHasEarlyConstraint= mHasLateConstraint = true;return this;
}/*** 最小15分钟 如果值比15分钟大取大的值*/
public longgetIntervalMillis() {return intervalMillis >= getMinPeriodMillis() ?intervalMillis : getMinPeriodMillis();
}
1.getIntervalMillis()最小值15分钟
2.getFlexMillis()最小值15分钟
3.getMinFlexMillis最小值5分钟
public final class JobSchedulerService extendscom.android.server.SystemServiceimplementsStateChangedListener, JobCompletedListener {//...
862 /**863 * Called when we have a job status object that we need to insert in our
864 * {@linkcom.android.server.job.JobStore}, and make sure all the relevant controllers know
865 * about.
866*/
867 private voidstartTrackingJob(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}876controller.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 booleanstopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,887 booleanwriteBack) {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
894 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);895}896}897 returnremoved;898}899}1026 /**1027 * A job just finished executing. We fetch the
1028 * {@linkcom.android.server.job.controllers.JobStatus} from the store and depending on
1029 * whether we want to reschedule we readd it to the controllers.
1030 *@paramjobStatus Completed job.
1031 *@paramneedsReschedule Whether the implementing class should reschedule this job.
1032*/
1033@Override1034 public void onJobCompleted(JobStatus jobStatus, booleanneedsReschedule) {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.
1046mHandler.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);1055startTrackingJob(rescheduled, jobStatus);1056 } else if (jobStatus.getJob().isPeriodic()) {//如果JobInfo调用setPeriodic会设置true
1057 JobStatus rescheduledPeriodic =getRescheduleJobForPeriodic(jobStatus);1058startTrackingJob(rescheduledPeriodic, jobStatus);1059}1060reportActive();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 {@linkJobStatus#NO_LATEST_RUNTIME}, but specify that any
951 * ready job with {@linkJobStatus#numFailures} > 0 will be executed.
952 *
953 *@paramfailureToReschedule Provided job status that we will reschedule.
954 *@returnA newly instantiated JobStatus with the same constraints as the last job except
955 * with adjusted timing constraints.
956 *
957 *@seeJobHandler#maybeQueueReadyJobsForExecutionLockedH
958*/
959 privateJobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {960 final long elapsedNowMillis =SystemClock.elapsedRealtime();961 final JobInfo job =failureToReschedule.getJob();962
963 final long initialBackoffMillis =job.getInitialBackoffMillis();964 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;965 longdelayMillis;966
967 switch(job.getBackoffPolicy()) {968 caseJobInfo.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 caseJobInfo.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
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 * {@linkcom.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 *@returnA new job representing the execution criteria for this instantiation of the
1000 * recurring job.
1001*/
1002 privateJobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {1003 final long elapsedNow =SystemClock.elapsedRealtime();1004 //Compute how much of the period is remaining.
1005 long runEarly = 0L;1006
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;1015
1016 if(DEBUG) {1017 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1018 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");1019}1020 return newJobStatus(periodicToReschedule, newEarliestRunTimeElapsed,1021 newLatestRuntimeElapsed, 0 /*backoffAttempt*/);1022}//...
}
3.如果设置obFinished(parameters,true)和obFinished(parameters,false)有什么区别?
这里关键在onJobCompleted
首先调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除,因为之前已经移除过了,所以这个 stopTrackingJob 的返回值为 false
@Overridepublic void onJobCompleted(JobStatus jobStatus, booleanneedsReschedule) {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
privateJobStatus 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;longdelayMillis;switch (job.getBackoffPolicy()) {//是线性还是指数级别策略
caseJobInfo.BACKOFF_POLICY_LINEAR:
delayMillis= initialBackoffMillis * backoffAttempts;//随着失败的次数越多这个值也越大
break;default:if(DEBUG) {
Slog.v(TAG,"Unrecognised back-off policy, defaulting to exponential.");
}caseJobInfo.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
StateController controller=mControllers.get(ic);
controller.rescheduleForFailure(newJob, failureToReschedule);
}returnnewJob;
}
看完这段代码可以回答
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方法
privateJobStatus 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 newJobStatus(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 voidstartJobScheduler() {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);//开始定时执行该系统任务
}