zoukankan      html  css  js  c++  java
  • 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<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();
    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<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;
    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<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); //开始定时执行该系统任务
        }
  • 相关阅读:
    置换及Polya定理
    题解 UVa10943
    Error applying site theme: A theme with the name "Jet 1011" and version already exists on the server.
    用shtml来include网页文件
    SQL 2005 附加数据库出错"解决方法
    SVN 配置 入门教程
    Oracle .Net Develoer
    JdbcTemplate完全学习
    SVD外积展开式
    初识 Nslookup 命令
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/8359573.html
Copyright © 2011-2022 走看看