Android 9.0 JobScheduler(三) 从Job的创建到执行

上一篇文章:JobScheduler(二)——JobScheduler框架介绍及JSS的启动

现在来看看Job的添加流程,当应用程序通过JobScheduler.schedule(jobinfo)添加一个Job,到它最终完成调度,这个过程是怎样的呢?现在就来看看这个过程。

首先来看其时序图:

Android 9.0 JobScheduler(三) 从Job的创建到执行_第1张图片

受限于图片大小,以上时序图中对一些流程进行了省略,只画出了重要的步骤,不过所有的步骤,都会在下面的内容分析中说明。关于以上流程中涉及到的类,在上一篇文章中都进行了说明。下面开始对该图流程进行分解。

1.客户端创建Job

一个应用需要通过Job执行任务时,通过JobScheduler来完成:

JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
private ComponentName mServiceComponent;
//根据JobService创建一个ComponentName对象
mServiceComponent = new ComponentName(this, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
builder.setMinimumLatency(1000);//设置延迟调度时间
builder.setOverrideDeadline(2000);//设置最大延迟截至时间
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);//设置所需网络类型
builder.setRequiresDeviceIdle(true);//设置在DeviceIdle时执行Job
builder.setRequiresCharging(true);//设置在充电时执行Job
builder.setExtras(extras);//设置一个额外的附加项
mJobScheduler.schedule(builder.build());//调度Job

JobScheduler.schedule(jobinfo)时,实际调用的是JobSchedulerImpl中的schedule()方法,该方法如下:

    @Override
    public int schedule(JobInfo job) {
        try {
            //调用进入JobSchedulerService
            return mBinder.schedule(job);
        } catch (RemoteException e) {
            return JobScheduler.RESULT_FAILURE;
        }
    }

在该方法中,mBinder就是JobSchedulerService.JobSchedulerStub类的实例,因为在注册JobSchedulerService服务时,在SystemServiceRegistry.java中:

        registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
                new StaticServiceFetcher<JobScheduler>() {
            @Override
            public JobScheduler createService() throws ServiceNotFoundException {
                IBinder b = ServiceManager.getServiceOrThrow(Context.JOB_SCHEDULER_SERVICE);
                return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
            }});

在这里,首先通过ServiceManager.getServiceOrThrow()拿到JobSchedulerStub对象,然后作为实力化JobSchedulerImpl的参数传入。

接着下一步分析,接下来将进入JobSchedulerService.JobSchedulerStub的schedule()中:

       @Override
        public int schedule(JobInfo job) throws RemoteException {
            if (DEBUG) {
                Slog.d(TAG, "Scheduling job: " + job.toString());
            }
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            final int userId = UserHandle.getUserId(uid);

            //权限检查
            enforceValidJobRequest(uid, job);
            //如果通过JobInfo.Builder.setPersisted(true)设置了该Job在每次启动时执行,则对应应用需要RECEIVE_BOOT_COMPLETED权限
            if (job.isPersisted()) {
                if (!canPersistJobs(pid, uid)) {
                    throw new IllegalArgumentException("Error: requested job be persisted without"
                            + " holding RECEIVE_BOOT_COMPLETED permission.");
                }
            }

            //校验携带的标记是否合理
            validateJobFlags(job, uid);

            long ident = Binder.clearCallingIdentity();
            try {
                return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
                        null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

在以上方法中 ,只有一个功能:检查权限是否授予,检查标记是否正确,然后将调用进入JobSchedulerService中的scheduleAsPackage()方法中,该方法如下:

2.Binder调用进入服务端(JobSchedulerService)

    public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
            int userId, String tag) {
        try {
            //根据包名判断该应用是否允许启动,如果该应用禁用,则return RESULT_FAILURE
            if (ActivityManager.getService().isAppStartModeDisabled(uId,
                    job.getService().getPackageName())) {
                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
                        + " -- package not allowed to start");
                return JobScheduler.RESULT_FAILURE;
            }
        } catch (RemoteException e) {
        }

        synchronized (mLock) {
            //查找是否已存在该Job(先找uid,再找uid中的jobId)
            final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());

            //初始化时work == null toCancel == null,故不会执行该if
            if (work != null && toCancel != null) {
                if (toCancel.getJob().equals(job)) {
                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
                    toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
                    return JobScheduler.RESULT_SUCCESS;
                }
            }

            //1.根据JoInfo创建一个JobStatus对象,JobStatus是job在内部的唯一标识
            JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
            //2.对于没有时间限制,且当前应用处于active时,会对job添加一个INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION标记
            jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);

            //每个应用最多只能有100个Job
            if (ENFORCE_MAX_JOBS && packageName == null) {
                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
                    Slog.w(TAG, "Too many jobs for uid " + uId);
                    throw new IllegalStateException("Apps may not schedule more than "
                                + MAX_JOBS_PER_APP + " distinct jobs");
                }
            }
            //设置为准备状态,并申请URI权限
            jobStatus.prepareLocked(ActivityManager.getService());

            if (toCancel != null) {
                cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
            }
            if (work != null) {
                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
            }
            //3.开始跟踪记录这个Job
            startTrackingJobLocked(jobStatus, toCancel);

            //4.如果Job此时已经满足其调度的所有条件,则加入执行列表中调度它
            //否则什么也不做,等待控制器触发监听后改变其状态
            if (isReadyToBeExecutedLocked(jobStatus)) {
                // This is a new job, we can just immediately put it on the pending
                // list and try to run it.
                //mJobPackageTracker记录
                mJobPackageTracker.notePending(jobStatus);
                //5.添加到"将要运行job"队列中
                addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
                //6.运行队列中的Job
                maybeRunPendingJobsLocked();
            }
        }
        return JobScheduler.RESULT_SUCCESS;//返回给客户端
    }

这个方法较长,并且其中部分逻辑不属于添加Job时的,所以下面对添加Job相关的逻辑分别进行总结,共有以下几个:

2.1.创建Job内部唯一标识——JobStatus

在应用中创建一个Job时,是以JobInfo为“单位”进行,而进入Framework层后,Job的唯一标识为JobStatus,每一个Job都对应一个唯一的JobStatus对象,JobStatus对象也正是在这个方法中实例化的,来看看如何实例化:

    public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg,
            int sourceUserId, String tag) {
        //当前时间
        final long elapsedNow = sElapsedRealtimeClock.millis();
        //Job运行最早/最晚时间点
        final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
        if (job.isPeriodic()) {//判断是否是定期Job
            //由setPeriodic()方法设置的
            latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
            //Job运行最早时间点
            earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
        } else {
            //如果不是定期Job,分别加setMinimumLatency()和setOverrideDeadline()设置的时间值
            earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
                    elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
            latestRunTimeElapsedMillis = job.hasLateConstraint() ?
                    elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
        }
        //客户端JobService完全限定名
        String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();

        //获取该应用处于哪个待机桶中(standbyBucket,应用待机桶,将应用划分在5个桶中,不同的桶有不同的省电策略)
        int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                sourceUserId, elapsedNow);
        JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
        //获取心跳时间???
        long currentHeartbeat = js != null
                ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
                : 0;
        //实例化一个JobStatus对象
        return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
                standbyBucket, currentHeartbeat, tag, 0,
                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
                /*innerFlags=*/ 0);
    }

在JobStatus构造方法中,初始化了一些全局变量,这里只说明一个全局变量:会将创建Job时在JobInfo设置的所有约束条件通过按位或运算,标记在全局变量requiredConstraints上:

        //从JobInfo中获取Job约束条件
        int requiredConstraints = job.getConstraintFlags();
        //获取网络限制
        if (job.getRequiredNetwork() != null) {
            requiredConstraints |= CONSTRAINT_CONNECTIVITY;
        }
        //获取最早执行时间限制
        if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
            requiredConstraints |= CONSTRAINT_TIMING_DELAY;
        }

requiredConstraints表示一个Job所需的所有约束,在后续的逻辑中判断一个Job是否有约束时,将通过requiredConstraints按位与运算进行,比如:

    //判断是否有充电条件限制
    public boolean hasChargingConstraint() {
        return (requiredConstraints & CONSTRAINT_CHARGING) != 0;
    }

此外,JobStatus中还有一个全局变量satisfiedConstraints,这个表示该Job当前满足的约束。

2.2.决策是否豁免该Job

是否对一个Job豁免,通过添加一个标记来判断:

    /**
     * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the 
     * job service (not necessarily the caller) was in the foreground and the job has no
     * time constraints, which makes it exempted from the battery saver job restriction.
     * @hide
     */
    //此标记表示没有时间限制的Job,当该job所属应用处于前台时,该Job将被调度执行
    public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;

如果确定对一个Job,那就意味着将不受省电管理的限制,当该Job所在应用处于前台时,就可以运行该Job。

判断是否豁免,通过JobStatus的maybeAddForegroundExemption()进行,该方法如下:

    public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
        //对于有时间限制的Job,不进行豁免
        if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
            return;
        }
        //如果存在此标记,则说明已经进行了豁免,直接return
        if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
            return;
        }
        //如果对应应用处于活动状态,则添加相关标记
        if (uidForegroundChecker.test(getSourceUid())) {
            addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
        }
    }

豁免标记,主要是为了从省电管理机制中豁免该应用,这样在进入某些省电状态时,就不会限制该Job的运行了。如果在创建Job的时候,通过setMinimumLatency()setOverrideDeadline()设置过限制Job调度的时间,那么是不会添加这个豁免标记的。

2.3.跟踪记录该Job

每向JobSchedulerService中添加一个Job,都将对该Job进行跟踪和记录,而这个工作,全部由StateController的派生类负责,当各个StateController中监测到相关连的条件发生改变时,将触发回调,然后符合条件的Job将执行,这部分代码如下:

    private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
        if (!jobStatus.isPreparedLocked()) {
            Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
        }
        //记录入队时间
        jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
        //添加到JobStore中,如果已存在则更新
        final boolean update = mJobs.add(jobStatus);
        if (mReadyToRock) {
            //各个控制器中开始跟踪这个Job
            for (int i = 0; i < mControllers.size(); i++) {
                StateController controller = mControllers.get(i);
                if (update) {//更新,则先停止
                    controller.maybeStopTrackingJobLocked(jobStatus, null, true);
                }
                //开始跟踪
                controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
            }
        }
    }

更详细的StateController在接下来的文章中分析,这里我们以DeviceIdlerController中的实现为例:

    @Override
    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
        //通过JobInfo.setImportantWhileForeground()设置的标记
        if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
            //添加到Doze豁免列表中
            mAllowInIdleJobs.add(jobStatus);
        }
        //更新Job有关Doze限制的标记
        updateTaskStateLocked(jobStatus);
    }

    private boolean updateTaskStateLocked(JobStatus task) {
        //是否豁免在Doze模式限制
        final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
                && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
        //Job所属应用是否处于Doze白名单中
        final boolean whitelisted = isWhitelistedLocked(task);
        //Job是否允许调度:Doze模式不处于IDLE状态或应用处于白名单中或应用处于前台且带有FLAG_IMPORTANT_WHILE_FOREGROUND
        final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
        //设置Doze模式约束条件
        return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
    }

setDeviceNotDozingConstraintSatisfied()方法中,将对参数enableTask和JobStatus全局变量satisfiedConstraints进行按位运算,satisfiedConstraints表示该Job当前满足的约束:

    boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
        dozeWhitelisted = whitelisted;
        return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
    }
    boolean setConstraintSatisfied(int constraint, boolean state) {
        boolean old = (satisfiedConstraints & constraint) != 0;
        if (old == state) {
            return false;
        }
        satisfiedConstraints = (satisfiedConstraints & ~constraint) | (state ? constraint : 0);
        return true;
    }

在以上方法中,将参数设置添加到satisfiedConstraints上,或者从satisfiedConstraints上清除,而在接下来的流程中,将通过satisfiedConstraints判断是否满足指定约束。

2.4.决策是否运行Job

当有新的Job时,是直接运行呢?还是暂不运行,当满足某一条件再运行呢?这是通过isReadyToBeExecutedLocked()进行决策的:

    private boolean isReadyToBeExecutedLocked(JobStatus job) {
        //该Job是否已准备好运行
        final boolean jobReady = job.isReady();
        //未准备好,直接返回false
        if (!jobReady) {
            return false;
        }

        //检查在mJobs中是否已添加了当前job,这是在`startTrackingJobLocked()`中添加
        final boolean jobExists = mJobs.containsJob(job);

        //获取UserID
        final int userId = job.getUserId();
        //该User是否启动过
        final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
        //不存在Job或者当该Job所对应User不存在,返回false(该条件一般都不可能)
        if (!jobExists || !userStarted) {
            return false;
        }

        //当前job是否已经处于待执行Job列表中
        final boolean jobPending = mPendingJobs.contains(job);
        //当前job是否正在运行
        final boolean jobActive = isCurrentlyActiveLocked(job);

        //如果当前job处于待执行队列中或者正在运行,返回false
        if (jobPending || jobActive) {
            return false;
        }

        //appStandby不处于“赦免”状态 && 该job所属应用不处于活动状态&&未设置AppStandby豁免标记
        if (!mInParole
                && !job.uidActive
                && !job.getJob().isExemptedFromAppStandby()) {
            //获取job所属应用处于哪个待机桶中
            final int bucket = job.getStandbyBucket();

            //如果心跳数小于该应用所处待机桶规定的心跳数,则不会触发执行Job,将延迟这个Job
            if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
                //获取该Job所属应用上次的运行Job时的心跳数
                final long appLastRan = heartbeatWhenJobsLastRun(job);
                //如果Job所属app处于NEVER待机桶中,且心跳数未达到该应用待机桶规定心跳数,那就就不执行它了,给他设置一个推迟时间点,方便判断
                if (bucket >= mConstants.STANDBY_BEATS.length
                        || (mHeartbeat > appLastRan
                                && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
                    if (job.getWhenStandbyDeferred() == 0) {
                        //返回false,推迟该Job的执行,设置Job推迟时间点为当前
                        job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
                    }
                    return false;
                } else {//说明当前心跳数已经大于等于待机桶中规定心跳数,将不会推迟Job
                }
            }
        }

        //验证此时Job所属应用包和service是否可用   
        final boolean componentPresent;
        try {
            //验证此时Job所属应用包和service是否可用
            componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
                    job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                    userId) != null);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
        return componentPresent;
    }

在以上方法中,刚开始就调用了JobStatus.isReady()方法,用于判断该Job是否已准备好运行,它由多个因素决定,比如设置有截至期限的Job(不管任何条件,只要到达截至期限后,必须运行)等等,这个判断就是通过isReady()来进行的,而且这些判断,就是通过获取上面所说的satisfiedConstraints实现,该方法如下:

    public boolean isReady() {
        //截至期限是否满足:非定期Job&&设置过overdeadline&&已经到达截至期限时间
        final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
                && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
        //Doze模式是否处于非IDLE状态:不处于IDLE状态&&没有设置FLAG_WILL_BE_FOREGROUND标记
        final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
                || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
        //是否对后台网络没有限制进行了豁免(appStandby限制)
        final boolean notRestrictedInBg =
                (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
        return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
    }

isConstraintsSatisfied()方法用来判断当前约束是否已满足所需所有的约束:

    public boolean isConstraintsSatisfied() {
        //该Job所有的约束条件
        final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
        //该Job现在所满足的约束条件
        int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
        //相等,说明当前所满足约束已是所需所有约束
        return (sat & req) == req;
    }

一句话概括isReady()方法:如果满足所有限制条件或已经到达截至期限,且不处于Doze状态且appStandby机制无后台限制且,说明它已准备好。在JobScheduler的使用时说道,使用setOverrideDeadline()方法设置此Job的最大延迟调度,当到达截至时间后,无论任何条件,都会执行该Job,就是在这里判断的。

回到isReadyToBeExecutedLocked(),如果该方法返回true,说明该Job已经准备好执行了,将开始执行Job,如果返回false,那么就没有其他事情要做了,只有等待它的一个控制器改变状态并适当地调度Job。假设该方法返回true,来接着分析执行流程。

2.5.为Job分配JobServiceContext

当该Job已准备好执行时,将调用maybeRunPendingJobsLocked()方法,开始执行Job,在调用该方法前,首先会将Job添加到待执行队列中:

addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);

maybeRunPendingJobsLocked()方法如下:

    private void maybeRunPendingJobsLocked() {
        if (DEBUG) {
            Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
        }
        //为待执行队列中的Job分配JobServiceContext并执行
        assignJobsToContextsLocked();
        //向DeviceIdleController报告Job状态
        reportActiveLocked();
    }

这个方法中依次调用了两个方法,其中reportActiveLocked()负责向DeviceIdleController报告是否有Job在运行,在Job执行时、完成时、取消时,都会对其进行报告,这里就不详细说明,其核心逻辑如下:

    void reportActiveLocked() {
        //......
        if (mReportedActive != active) {
            mReportedActive = active;
            if (mLocalDeviceIdleController != null) {
                //向DeviceIdleController报告
                mLocalDeviceIdleController.setJobsActive(active);
            }
        }
    }

来看assignJobsToContextsLocked()方法:

   private void assignJobsToContextsLocked() {

        int memLevel;
        try {
            //获取系统当前内存级别
            memLevel = ActivityManager.getService().getMemoryTrimLevel();
        } catch (RemoteException e) {
            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
        }
        //根据系统当前内存状况决定最大运行Job数
        switch (memLevel) {
            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_LOW:
                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
                break;
            default:
                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
                break;
        }

        //临时分配的用于和mActiveServices中的JSC实例相映射的JobStatus实例数组
        //即contextIdToJobMap[i]在mActiveServices[i]上执行
        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
        //表示"是否在此JobServiceContext上执行job"的布尔数组
        boolean[] act = mTmpAssignAct;
        //表示"要将其作业分配给上下文的首选UID"的数组
        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
        //以上三个数组的大小均为MAX_JOB_CONTEXTS_COUNT=16
        //正在运行的Job数量
        int numActive = 0;
        //Job所属应用处于前台的数量
        int numForeground = 0;
        //遍历获取所有的JobServiceContext对象
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            //获取数组中实例化的JobServiceContext对象
            final JobServiceContext js = mActiveServices.get(i);
            //获取在该JobServiceContext对象上正在执行的Job,没有则返回null
            final JobStatus status = js.getRunningJobLocked();
            //将contextIdToJobMap[i]和status进行映射,且如果有Job正在运行,则将numActive +1
            if ((contextIdToJobMap[i] = status) != null) {
                numActive++;
                //如果优先级大于等于PRIORITY_TOP_APP,说明处于前台,numForeground +1
                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
                    numForeground++;
                }
            }
            //全部置为false
            act[i] = false;
            //获取各个JobServiceContext的首选Uid,如果未设置,默认为-1
            preferredUidForContext[i] = js.getPreferredUid();
        }
        //此时,每个JSC实例都和JobStatus进行了映射:contextIdToJobMap[i] -> status,而且status也许为null

        //遍历待执行Job队列,为每个待执行Job分配JSC实例
        for (int i=0; i<mPendingJobs.size(); i++) {
            JobStatus nextPending = mPendingJobs.get(i);

            //如果nextPending已经存在于contextIdToJobMap中,则说明该Job已经运行,处理下一个Job
            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
            if (jobRunningContext != -1) {
                continue;
            }

            //调整并设置Job优先级,各个Job默认优先级为`PRIORITY_DEFAULT`(0),当Job所属应用处于前台时,将会设置该uid的优先级为`PRIORITY_TOP_APP`
            final int priority = evaluateJobPriorityLocked(nextPending);
            nextPending.lastEvaluatedPriority = priority;

            //找一个可用的JSC或着将优先级最低的JSC腾出来给这个Job
            int minPriority = Integer.MAX_VALUE;
            //优先级最小的JobScheduleContext id
            int minPriorityContextId = -1;
            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
                // 得到JobStatus
                JobStatus job = contextIdToJobMap[j];
                //得到首选执行Job所对应的Uid
                int preferredUid = preferredUidForContext[j];
                //说明contextIdToJobMap[j]为空
                if (job == null) {
                    //如果存在未被占用的JSC,且没有设置首选uid,那么就是它了,就用它来执行这个Job,否则,找下一个吧
                    //(正在执行Job数量小于最大执行Job数量 || (应用处于前台&&当前前台Job数小于最大前台执行数)) &&
                    //(该Job所属应用为该JobServiceContext首选应用|| 该JobServiceContext首选应用为默认值-1)
                    if ((numActive < mMaxActiveJobs ||
                            (priority >= JobInfo.PRIORITY_TOP_APP &&
                                    numForeground < mConstants.FG_JOB_COUNT)) &&
                            (preferredUid == nextPending.getUid() ||
                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
                        minPriorityContextId = j;
                        break;
                    }

                    //继续下一次循环
                    continue;
                }
                //如果队列中的job所属应用和contextIdToJobMap中的不同,直接进行下一次循环
                if (job.getUid() != nextPending.getUid()) {
                    continue;
                }
                //如果job优先级大于队列中Job的优先级,直接进行下一次循环
                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
                    continue;
                }
                //如果能到这,说明16个JSC都没空,那么将nextPending的优先级设为最小优先级,从而得到优先级最小的JSC
                if (minPriority > nextPending.lastEvaluatedPriority) {
                    minPriority = nextPending.lastEvaluatedPriority;
                    minPriorityContextId = j;
                }
            }
            //找到优先级最低的JSC,将待执行的Job和它进行映射
            if (minPriorityContextId != -1) {
                contextIdToJobMap[minPriorityContextId] = nextPending;
                act[minPriorityContextId] = true;//需要执行Job的JSC将可执行
                numActive++;//正在执行Job数 +1
                if (priority >= JobInfo.PRIORITY_TOP_APP) {
                    numForeground++;//前台执行Job数 +1
                }
            }
        }
        mJobPackageTracker.noteConcurrency(numActive, numForeground);
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            boolean preservePreferredUid = false;
            if (act[i]) {
                //正在运行的Job
                JobStatus js = mActiveServices.get(i).getRunningJobLocked();
                //如果存在正在运行的Job
                if (js != null) {
                    //如果此JSC上有正在运行的Job,则先进行停止,但将这个Job所属应用继续保留为首选应用
                    mActiveServices.get(i).preemptExecutingJobLocked();
                    preservePreferredUid = true;
                //如果不存在正在运行的Job,则从contextIdToJobMap中取出为该JSC事先分配的JobStatus对象
                } else {
                    final JobStatus pendingJob = contextIdToJobMap[i];
                    //通过各个控制器,做好运行Job的准备,实际上只有ContentObserverController对他进行了实现
                    for (int ic=0; ic<mControllers.size(); ic++) {
                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
                    }
                    //开始执行Job,执行成功返回true
                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
                        Slog.d(TAG, "Error executing " + pendingJob);
                    }
                    //从待执行Job列表中移除该Job
                    if (mPendingJobs.remove(pendingJob)) {
                        mJobPackageTracker.noteNonpending(pendingJob);
                    }
                }
            }
            //清除首选Uid
            if (!preservePreferredUid) {
                mActiveServices.get(i).clearPreferredUid();
            }
        }
    }

在以上方法中,将会为待执行队列中的Job分配JobServiceContext对象(以下简称JSC),我们之前说过,JJobServiceContext处理应用中JobService的绑定和Job的生命周期,且一个JobServiceContext对象只能执行一个Job。正因如此,在上述方法中,将从JSC列表中,查找剩余的JSC实例,如果JSC实例都已经分配了Job了,那么将根据Job的优先级,最小优先级的Job只能倒霉了,它将被优先级高的Job替换。而且如果它正在运行,则将被停止。或许是出于不好意思,将正在运行的低优先级Job取消后,会将其所属应用作为首选应用,仿佛在说:“下次肯定轮到你”,然而还是取决于优先级大小。

当分配成功后,将调用JSC的executeRunnableJob(pendingJob)方法,开始启动Job。

2.6.绑定JSC和Service

我们说过,Job的执行,是在对应应用的进程中执行,为何在对应应用进程执行,是因为在JSC中,通过bindService的方式启动了服务,而且JSC本身就是一个SeviceConnection,所以当绑定Service成功后,将回调onServiceConnected(),解除绑定后将回调onServiceDisconnected()方法,在Service中,绑定后将出发生命周期方法中的onBind()方法…先来看启动Job的executeRunnableJob(pendingJob)

    boolean executeRunnableJob(JobStatus job) {
        synchronized (mLock) {
            //首先检查该JSC实例是否可用,当该JSC上已经在运行Job时mAvailable为false
            if (!mAvailable) {
                return false;
            }
            //清空JSC实例的首选应用id
            mPreferredUid = NO_PREFERRED_UID;
            //表示正在此JSC实例上运行的Job
            mRunningJob = job;
            //实例化一个回调接口
            mRunningCallback = new JobCallback();
            //是否到达截至期限
            final boolean isDeadlineExpired =
                    job.hasDeadlineConstraint() &&//是否设置了截至期限
                            (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis());//截至期限时间点
            //???
            Uri[] triggeredUris = null;
            if (job.changedUris != null) {
                triggeredUris = new Uri[job.changedUris.size()];
                job.changedUris.toArray(triggeredUris);
            }
            //???
            String[] triggeredAuthorities = null;
            if (job.changedAuthorities != null) {
                triggeredAuthorities = new String[job.changedAuthorities.size()];
                job.changedAuthorities.toArray(triggeredAuthorities);
            }
            final JobInfo ji = job.getJob();
            //实例化JobParameters,将作为回调方法参数
            mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
                    ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
                    isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
            //设置开始执行Job时间
            mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();

            //获取由appStandby导致Job推迟的时间
            final long whenDeferred = job.getWhenStandbyDeferred();
            if (whenDeferred > 0) {
                //如果存在推迟时间,说明该job被appStandy推迟过,得到推迟的具体时长,然后打个log就完事了...
                final long deferral = mExecutionStartTimeElapsed - whenDeferred;
                if (DEBUG_STANDBY) {
                }
            }

            //???
            job.clearPersistedUtcTimes();

            //设置Job和客户端交互状态为绑定状态,有绑定-启动-执行-停止-完成五个,VERB_BINDING表示开始绑定
            mVerb = VERB_BINDING;
            // 当向客户端发送消息时,我们无法控制其执行,因此通过该方法,设置一个确定的时长,
            // 如果当达到一定时间内没有收到回应,则取消该动作,继续其他流程
            // 和它对应的是removeOpTimeOutLocked(),用来取消定时
            scheduleOpTimeOutLocked();
            //创建Intent
            final Intent intent = new Intent().setComponent(job.getServiceComponent());
            //绑定应用对应的Service并启动
            boolean binding = mContext.bindServiceAsUser(intent, this,
                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
                    new UserHandle(job.getUserId()));
            //如果绑定失败
            if (!binding) {
                mRunningJob = null;//置空
                mRunningCallback = null;
                mParams = null;
                mExecutionStartTimeElapsed = 0L;
                mVerb = VERB_FINISHED;//Job交互状态设置为完成
                removeOpTimeOutLocked();//移除定时
                return false;
            }
            //绑定成功
            mJobPackageTracker.noteActive(job);
            try {
                //通知BatteryStatsService Job运行,以用于统计耗电
                mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
            } catch (RemoteException e) {
                // Whatever.
            }
            final String jobPackage = job.getSourcePackageName();
            final int jobUserId = job.getSourceUserId();
            UsageStatsManagerInternal usageStats =
                    LocalServices.getService(UsageStatsManagerInternal.class);
            //通知USageStatsService最后Job的运行时间
            usageStats.setLastJobRunTime(jobPackage, jobUserId, mExecutionStartTimeElapsed);
            JobSchedulerInternal jobScheduler =
                    LocalServices.getService(JobSchedulerInternal.class);
            //通知JobSchedulerService Job开始运行
            jobScheduler.noteJobStart(jobPackage, jobUserId);
            mAvailable = false;//表示该JSC实例将不可用
            mStoppedReason = null;
            mStoppedTime = 0;
            return true;
        }
    }

在以上方法中,绑定Service之前,首先要判断当前JSC对象是否已经有Job在运行,如果是直接返回false,否则将开始绑定Service,并根据绑定结果,执行相应的操作。

下面就分别来看一下,通过Binder绑定Service后客户端和服务端的回调,首先来看客户端中第一个回调方法、也是属于生命周期方法的onBind(),在JobService中:

2.6.1.客户端onBind()回调

    public final IBinder onBind(Intent intent) {
        if (mEngine == null) {
            //实例化一个JobServiceEngine匿名对象
            mEngine = new JobServiceEngine(this) {
                @Override
                public boolean onStartJob(JobParameters params) {
                    return JobService.this.onStartJob(params);
                }

                @Override
                public boolean onStopJob(JobParameters params) {
                    return JobService.this.onStopJob(params);
                }
            };
        }
        //返回IJobService.Stub类实例
        return mEngine.getBinder();
    }

在JobService的onBind()方法中,将创建一个JobServiceEngine类的匿名对象,然后通过getBinder()返回一个IBinder对象,再确切一点,这个IBinder是JobServiceEngine.JobInterface对象。因为在JobServiceEngine的构造方法中:

    public JobServiceEngine(Service service) {
        mBinder = new JobInterface(this);
        mHandler = new JobHandler(service.getMainLooper());
    }

同时可以看到,当调用JobServiceEngine的onStartJob()onStopJob()方法时,将会调用JobService对应的onStartJob()onStopJob()方法,从而进入应用线程中执行。那么接下来,只需知道JobServiceEngine的这两个方法什么时候会执行,那么JobService中将开始执行这个Job。

2.6.2.服务端onServiceConnected()回调

分析完客户端回调后,再来看服务端绑定成功后回调的onServiceConnected()方法,在JobServiceContext中:

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        JobStatus runningJob;
        synchronized (mLock) {
            runningJob = mRunningJob;

            //做个小判断
            if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
                closeAndCleanupJobLocked(true /* needsReschedule */,
                        "connected for different component");
                return;
            }
            //IJobService实例
            this.service = IJobService.Stub.asInterface(service);
            //申请WakLock,系统将被唤醒
            final PowerManager pm =
                    (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                    runningJob.getTag());
            //为该Wakelock设置工作源,方面统计应用耗电(在Service代表应用程序执行工作的情况下很有用)
            wl.setWorkSource(deriveWorkSource(runningJob));
            //非计数锁
            wl.setReferenceCounted(false);
            wl.acquire();//申请WakeLock

            //如果之前的WakLock存在,则释放
            if (mWakeLock != null) {
                mWakeLock.release();
            }
            mWakeLock = wl;
            //启动服务
            doServiceBoundLocked();
        }
    }

在这个方法中,参数中的IBinder对象,则是客户端onBind()方法的返回值,在分析客户端回调时我们知道,这个返回值为JobServiceEngine.JobInterface实例,在这个方法中将这个实例赋值给了全部变量service。然后申请了一个WakeLock,使得系统唤醒或保持唤醒,接下来调用doServiceBoundLocked()去执行Job了。

6.3 进入应用线程的onstartJob()

现在我们接着上一步分析,来看看doServiceBoundLocked()方法:

    @GuardedBy("mLock")
    void doServiceBoundLocked() {
        //取消定时
        removeOpTimeOutLocked();
        //处理服务的绑定
        handleServiceBoundLocked();
    }

其中removeOpTimeOutLocked()只是取消防止耗时的定时,定时是为了提示系统,当超过这个时间后,将取消和该Job所对应JobService的交互:

    private void removeOpTimeOutLocked() {
        mCallbackHandler.removeMessages(MSG_TIMEOUT);
    }

我们将注意力放在handleServiceBoundLocked()中:

@GuardedBy("mLock")
private void handleServiceBoundLocked() {
    //Job交互状态不处于绑定状态时,取消了
    if (mVerb != VERB_BINDING) {
        closeAndCleanupJobLocked(false /* reschedule */, "started job not pending");
        return;
    }
    //如果调用cancel()等方法,取消该Job
    if (mCancelled) {
        closeAndCleanupJobLocked(true /* reschedule */, "cancelled while waiting for bind");
        return;
    }
    try {
        //Job交互状态由绑定状态变为启动状态
        mVerb = VERB_STARTING;
        //设置一个定时,超过时间将取消
        scheduleOpTimeOutLocked();
        //调用JobServiceEngine.JobInterface的startJob()
        service.startJob(mParams);
    } catch (Exception e) {
    }
}

在这个方法中,经过两个判断后,将Job交互状态由VERB_BINDING变为VERB_STARTING,意思是启动了,然后设置一个定时,最后调用JobServiceEngine.JobInterfacestartJob()方法,我们来看看这个方法:

//frameworks/base/core/java/android/app/job/JobServiceEngine.java

@Override
public void startJob(JobParameters jobParams) throws RemoteException {
    JobServiceEngine service = mService.get();
    if (service != null) {
        Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
        m.sendToTarget();
    }
}

在mHandler中:

@Override
public void handleMessage(Message msg) {
    final JobParameters params = (JobParameters) msg.obj;
    switch (msg.what) {
        case MSG_EXECUTE_JOB:
            try {
                //调用JobServiceEngine的onStartJob(),并获取返回值
                boolean workOngoing = JobServiceEngine.this.onStartJob(params);
                //将回调JSC中,确认启动信息,将根据workOngoing决定是否继续执行该Job
                ackStartMessage(params, workOngoing);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            break;

handleMessage()中,首先将调用JobServiceEngine的onStartJob(),而在绑定服务的时候看到,JobServiceEngine的onStartJob()方法中又调用了JobService的onStartJob()方法,从而开始执行Job。

回到handleMessage()中,当执行完成onStartJob()后,开始执行ackStartMessage()方法,并将onStartJob()方法的返回值作为其参数。经过一系列的调用:ackStartMessage()->JobServiceContext.acknowledgeStartMessage()->doAcknowledgeStartMessage()->doCallback()->doCallbackLocked(),最终是调用进入到了JobServiceContext的doCallbackLocked()方法中,来看下这个方法:

6.4.onStartJob()完毕后的收尾

    @GuardedBy("mLock")
    void doCallbackLocked(boolean reschedule, String reason) {
        removeOpTimeOutLocked();//移除定时

        if (mVerb == VERB_STARTING) {//Job交互状态为启动
            handleStartedLocked(reschedule);
        } else if (mVerb == VERB_EXECUTING ||
                mVerb == VERB_STOPPING) {
            handleFinishedLocked(reschedule, reason);
        } else {
        }
    }

根据当前方法的执行,此时Job和客户端交互状态为VERB_STARTING,因此将通过handleStartedLocked(),切换Job的交互状态,并根据onStartJob()的返回值决定是否停止该Job,该方法如下:

    @GuardedBy("mLock")
    private void handleStartedLocked(boolean workOngoing) {
        switch (mVerb) {
            case VERB_STARTING:
                //Job交互状态由VERB_START变为VERB_EXECUTING
                mVerb = VERB_EXECUTING;
                //也就是说如果onStartJob()返回false,则将关闭Job和客户端的绑定
                if (!workOngoing) {
                    handleFinishedLocked(false, "onStartJob returned false");
                    return;
                }
                if (mCancelled) {
                    // Cancelled *while* waiting for acknowledgeStartMessage from client.
                    handleCancelLocked(null);
                    return;
                }
                scheduleOpTimeOutLocked();//设置VERB_EXECUTING状态定时
                break;
            default:
                return;
        }
    }

在以上方法中,首先将Job和客户端交互状态由启动转变成了执行,然后,如果onStartJob()返回false,则将关闭Job和客户端的绑定,这将说明一个Job的调度已经完成,如果onStartJob()返回true,那么此时不会立即关闭和客户端的绑定,而是设置一个VERB_EXECUTING状态的定时这个定时为600s,当到达时间后,进入STOPPING状态,并调用客户端onStopJob()方法,最终会在8s后进入FINISHED状态。

如果onStartJob()返回false,则将执行handleFinishedLocked(),该方法如下:

    @GuardedBy("mLock")
    private void handleFinishedLocked(boolean reschedule, String reason) {
        switch (mVerb) {
            case VERB_EXECUTING:
            case VERB_STOPPING:
                //关闭当前JobServiceContext和JobService的连接
                closeAndCleanupJobLocked(reschedule, reason);
                break;
        }
    }

在这个方法中又调用了closeAndCleanupJobLocked()方法,该方法如下:

    @GuardedBy("mLock")
    private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
        final JobStatus completedJob;
        if (mVerb == VERB_FINISHED) {
            return;
        }
        applyStoppedReasonLocked(reason);
        completedJob = mRunningJob;
        mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
        try {
            mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
                    mRunningJob.getSourceUid(), mParams.getStopReason());
        } catch (RemoteException e) {
            // Whatever.
        }
        //释放WakeLock
        if (mWakeLock != null) {
            mWakeLock.release();
        }
        //解除该JobServiceContext实例和JobService的绑定
        mContext.unbindService(JobServiceContext.this);
        //置空对象
        mWakeLock = null;
        mRunningJob = null;
        mRunningCallback = null;
        mParams = null;
        mVerb = VERB_FINISHED;//Job交互状态由
        mCancelled = false;
        service = null;
        mAvailable = true;
        removeOpTimeOutLocked();//移除定时
        //回调
        mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
    }

在以上方法中,首先将释放WakeLock,接下来将通过unbindService()解除该JSC实例和JobService的绑定,再接下来置空了一些对象,然后将Job交互状态由VERB_EXECUTE变为VERB_FINISHED,并移除前一个交互状态的定时,最后,将回调mCompletedListener.onJobCompletedLocked()方法。而这个方法的实现,在JSS中:

    @Override
    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
        //是否需要重新执行?是:则根据原来JobStatus创建新的JobStatus实例
        final JobStatus rescheduledJob = needsReschedule
                ? getRescheduleJobForFailureLocked(jobStatus) : null;
        //停止跟踪Job,并从JobStore中移除,移除成功返回true
        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
            return;
        }

        //如果需要重新执行,则开始记录这个重新执行的新Job
        if (rescheduledJob != null) {
            try {
                rescheduledJob.prepareLocked(ActivityManager.getService());
            } catch (SecurityException e) {
            }
            startTrackingJobLocked(rescheduledJob, jobStatus);
        //如果是定期Job,也需要重新执行,则开始记录这个重新执行的新Job
        } else if (jobStatus.getJob().isPeriodic()) {
            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
            try {
                rescheduledPeriodic.prepareLocked(ActivityManager.getService());
            } catch (SecurityException e) {
            }
            startTrackingJobLocked(rescheduledPeriodic, jobStatus);
        }
        //uriPerms.revoke(am)
        jobStatus.unprepareLocked(ActivityManager.getService());
        //向DeviceIdleController报告Job状态
        reportActiveLocked();
        //Handler中重新进行入队操作
        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
    }

在这个回调方法中,会根据参数needsReschedule决定是否重新执行Job,需要注意的是,当Job完成后,该值为false(handleFinishedLocked()中传入)。我们在前面说过,当客户端onStopJob()方法返回ture时,将会重新执行该Job,正是这个参数的原因,当执行完onStopJob()方法后,最终也会进入这里,这部分在后续文章中说明。

总而言之,此方法主要有三个工作,其细节就不再详细分析:

  • 1.如果客户端执行JobService.onStopJob()返回true,则根据该Job重新创建一个新Job,并开始跟踪记录;
  • 2.如果刚执行的Job是一个定期Job(通过JobInfo.Builder.isPeriodic()设置),则根据该Job重新创建一个新的Job,并开始跟踪记录;
  • 3.通过Handler发送消息,调用queueReadyJobsForExecutionLocked()重新整顿Job队列。

3.总结

如此一来,一个Job从创建到执行完毕的过程,就分析完毕了。从整个流程来看,JobSchedulerService
中,负责Job前期的准备和执行后的回调工作,而Job的执行过程控制,则在JobServiceContext中,Job和客户端JobService交互的五种状态,我们可以理解为一个Job的生命周期:

  • VERB_BINDING:JobServiceContext和JoService开始绑定时的状态,该状态最长持续18s;
  • VERB_STARTING:JobServiceContext和JoService绑定成功,即将调用onStartJob()开始启动时的状态,该状态最长持续8s;
  • VERB_EXECUTING:onStartJob()执行完毕后的状态,如果该方法返回false,则该状态将结束,进入下一个状态,该状态最长持续600s;
  • VERB_STOPPING:当Job处于VERB_EXECUTING时,如果通过cancel()取消该Job时,将进入该状态,该状态下将回调客户端JobService的onStopJob()方法,该状态最长持续8s;
  • VERB_FINISHED:整个Job执行完毕后的状态,该状态下将解除Service绑定、释放WakeLock。当Job处于VERB_EXECUTINGVERB_STOPPING状态时会进入该状态,该状态时说明已经Job执行完毕。

而这些生命周期状态的限制时长,就是多次提到的定时:

    private void scheduleOpTimeOutLocked() {
        removeOpTimeOutLocked();

        final long timeoutMillis;
        switch (mVerb) {
            case VERB_EXECUTING:
                timeoutMillis = EXECUTING_TIMESLICE_MILLIS;//600s
                break;

            case VERB_BINDING:
                timeoutMillis = OP_BIND_TIMEOUT_MILLIS;//18s
                break;

            default:
                timeoutMillis = OP_TIMEOUT_MILLIS;//8s
                break;
        }
        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
        mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
    }

每成功执行一步,都会通过removeOpTimeOutLocked()方法移除定时,并设置一个新的定时:

    private void removeOpTimeOutLocked() {
        mCallbackHandler.removeMessages(MSG_TIMEOUT);
    }

当到达定时后,说明超时了,这时将通过handleOpTimeOutLocked()方法处理定时:

    @GuardedBy("mLock")
    private void handleOpTimeoutLocked() {
        switch (mVerb) {
            case VERB_BINDING://绑定客户端JobService时超时
                closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
                break;
            case VERB_STARTING://执行onStartJob()时超时
                closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
                break;
            case VERB_STOPPING://停止后超时
                closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
                break;
            case VERB_EXECUTING://执行Job时超时,典型的onStartJob()返回true时
                mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
                sendStopMessageLocked("timeout while executing");
                break;
            default:
                closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
        }
    }

下一篇文章:JobScheduler(四)——Job约束条件的控制

你可能感兴趣的:(Android系统开发)