java定时任务有的系统不生效_JobService 7.0 定时任务不生效

本文介绍了在Android 7.0中遇到的JobService定时任务不生效问题,并详细解析了JobScheduler的相关设置,包括最小执行间隔、回退策略、周期任务的重新调度等。在Android 7.0上,通过setPeriodic()设置的间隔会被强制限制为15分钟,若要实现更短间隔,需使用setMinimumLatency()。同时,文章探讨了setBackoffCriteria()的作用,它是用来设置失败后的重试时间和策略。对于周期任务,当任务完成时,根据是否需要重新调度,会采用不同的处理方式。
摘要由CSDN通过智能技术生成

代码

//构建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);//开始定时执行该系统任务

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值