JobService 7.0 定时任务不生效


代码

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

你可能感兴趣的:(疑难杂症,Android,7.0)