代码
// 构建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) { 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(); 962 963 final long initialBackoffMillis = job.getInitialBackoffMillis(); 964 final int backoffAttempts = failureToReschedule.getNumFailures() + 1; 965 long delayMillis; 966 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 ) { 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; 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 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) { 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); //开始定时执行该系统任务 }