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

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

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

首先来看其时序图:

Job的创建到执行

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

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
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值