JobScheduler之源码分析

文中的源代码版本为api23

JobScheduler工作流程

接下来我们从源码角度去深入理解JobScheduler的运行机制。 客户端调用JobScheduler.schedule方法之后,通过Binder通信会进入到JobSchedulerStub.schedule方法

1 JobScheduler工作流程

1.1 JobSchedulerStub.schedule方法
final class JobSchedulerStub extends IJobScheduler.Stub {
    public int schedule(JobInfo job) throws RemoteException {
        //log..
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();

        //主要是校验一下Service是否存在,是否拥有BIND_JOB_SERVICE权限
        enforceValidJobRequest(uid, job);
        if (job.isPersisted()) {
            //如果Job需要持久化,那需要校验是否有RECEIVE_BOOT_COMPLETED权限
            if (!canPersistJobs(pid, uid)) {
                throw new IllegalArgumentException("Error: requested job be persisted without"
                        + " holding RECEIVE_BOOT_COMPLETED permission.");
            }
        }

        long ident = Binder.clearCallingIdentity();
        try {
            //JobSchedulerStub是JobSchedulerService的内部类,后续流程
            //直接抛给JobSchedulerService来完成
            return JobSchedulerService.this.schedule(job, uid);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
}
复制代码

JobSchedulerStub.schedule方法的逻辑比较简单,主要干了两件事:

  1. 做一些权限校验
  2. 调用JobSchedulerService.schedule方法继续后续流程
1.2 JobSchedulerService.schedule方法
public int schedule(JobInfo job, int uId) {
    JobStatus jobStatus = new JobStatus(job, uId);
    cancelJob(uId, job.getId());
    startTrackingJob(jobStatus);
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    return JobScheduler.RESULT_SUCCESS;
}
复制代码

该方法的逻辑还是非常清晰的:

  1. 封装JobInfoJobStatus
  2. 取消具有相同jobId的老任务 3 调用startTrackingJob开始进行状态跟踪(重点)
  3. 发送一个MSG_CHECK_JOB消息

其中2、4步我们放到后面讲,先关注核心方法startTrackingJob

1.3 JobSchedulerService.startTrackingJob方法
private void startTrackingJob(JobStatus jobStatus) {
    boolean update;
    boolean rocking;
    synchronized (mJobs) {
        //mJobs是Job的管理者,类型为JobStore
        //mJobs内部使用ArraySet来保存JobStatus
        //JobStore.add方法涉及到ArraySet的remove和add操作
        //update就是ArraySet.remove方法的返回值
        //由于当前的JobStatus是全新的
        //因此此处的update为false
        update = mJobs.add(jobStatus);
        //mReadyToRock字段用来跟踪系统启动状态
        //一般情况下mReadyToRock都为true
        rocking = mReadyToRock;
    }
    if (rocking) {
        for (int i=0; i<mControllers.size(); i++) {
            //StateController
            StateController controller = mControllers.get(i);
            //...
            controller.maybeStartTrackingJob(jobStatus);
        }
    }
}
复制代码

该方法的核心逻辑是遍历所有的StateController并执行其maybeStartTrackingJob方法。 JobSchedulerService使用一个名为mControllers的变量保存StateController,其类型为List,在JobSchedulerService的构造函数中被初始化。

public JobSchedulerService(Context context) {
    super(context);
    // Create the controllers.
    mControllers = new ArrayList<StateController>();
    mControllers.add(ConnectivityController.get(this));
    mControllers.add(TimeController.get(this));
    mControllers.add(IdleController.get(this));
    mControllers.add(BatteryController.get(this));
    mControllers.add(AppIdleController.get(this));

    //...
}
复制代码

可以看到,StateController的派生类有很多,有ConnectivityControllerTimeControllerIdleControllerBatteryControllerAppIdleControllerJobSchedulerService正是通过这些StateController实现了对网络连接状态、时间、设备空闲状态、电池电量、应用空闲状态等的监听。 为了便于分析,我们以相对简单的AppIdleController为例,继续流程分析。

1.4 AppIdleController.maybeStartTrackingJob方法
public void maybeStartTrackingJob(JobStatus jobStatus) {
    synchronized (mTrackedTasks) {
        //mTrackedTasks为ArrayList<JobStatus>类型
        //AppIdleController使用该字段保存JobStatus
        mTrackedTasks.add(jobStatus);
        String packageName = jobStatus.job.getService().getPackageName();
        //通过UsageStatsService获取当前应用是否处于空闲状态
        final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                jobStatus.uId, jobStatus.getUserId());
        //debug log...
        //根据当前应用状态设置JobStatus的对应字段
        //appNotIdleConstraintSatisfied为AtomicBoolean类型
        //用来记录当前app的空闲状态
        jobStatus.appNotIdleConstraintSatisfied.set(!appIdle);
    }
}
复制代码

maybeStartTrackingJob逻辑比较简单,干了两件事情

  1. 保存JobStatus
  2. 根据当前应用状态设置JobStatus.appNotIdleConstraintSatisfied字段的值

那么流程到这里就断了么?显然不会,AppIdleController内部会一直跟踪应用状态,当应用状态发生变化时会通知JobSchedulerService

我们先来看看AppIdleController的构造函数

private AppIdleController(StateChangedListener stateChangedListener, Context context) {
    super(stateChangedListener, context);
    mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
    mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
    mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
}
复制代码

构造函数的逻辑非常简单,首先是获取UsageStatsService服务,然后注册了一个应用状态变更监听。

AppIdleStateChangeListenerAppIdleController的一个内部类,当应用状态发生变化时会回调其onAppIdleStateChanged方法,我们直接上代码

@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
    boolean changed = false;
    synchronized (mTrackedTasks) {
        //这个return逻辑先不管它
        if (mAppIdleParoleOn) {
            return;
        }
        for (JobStatus task : mTrackedTasks) {
            if (task.job.getService().getPackageName().equals(packageName)
                    && task.getUserId() == userId) {
                if (task.appNotIdleConstraintSatisfied.get() != !idle) {
                    //debug log...
                    //应用状态发生改变,更新对应的字段
                    task.appNotIdleConstraintSatisfied.set(!idle);
                    changed = true;
                }
            }
        }
    }
    if (changed) {
        //只要有Job的状态发生了变化就会触发回调
        mStateChangedListener.onControllerStateChanged();
    }
}
复制代码

逻辑非常简单,就是遍历所有的JobStatus看状态是否发生变化,如果是,则更新appNotIdleConstraintSatisfied字段。同时,只要有一个JobStatus的状态被更新,就会触发一个回调。

mStateChangedListener的实现类就是JobSchedulerService,由于只有一句代码,就不贴出来了。该方法就是通过JobHandler(JobSchedulerService的一个内部类,派生自Handler)发送了一个MSG_CHECK_JOB消息,接着就会依次触发maybeQueueReadyJobsForExecutionLockedHmaybeRunPendingJobsH

1.5 JobHandler.maybeQueueReadyJobsForExecutionLockedH方法
private void maybeQueueReadyJobsForExecutionLockedH() {
    int chargingCount = 0;
    int idleCount =  0;
    int backoffCount = 0;
    int connectivityCount = 0;
    List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
    //通过JobStore获取当前所有的Job
    ArraySet<JobStatus> jobs = mJobs.getJobs();
    for (int i=0; i<jobs.size(); i++) {
        JobStatus job = jobs.valueAt(i);
        //使用isReadyToBeExecutedLocked方法判断
        //当前的Job所以来的约束条件是否已经满足
        if (isReadyToBeExecutedLocked(job)) {
            if (job.getNumFailures() > 0) {
                //计算重试的Job数量
                backoffCount++;
            }
            if (job.hasIdleConstraint()) {
                //计算依赖设备空闲状态的Job数量
                idleCount++;
            }
            if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
                //计算依赖网络类型的Job数量
                connectivityCount++;
            }
            if (job.hasChargingConstraint()) {
                //计算依赖充电状态的Job数量
                chargingCount++;
            }
            runnableJobs.add(job);
        } else if (isReadyToBeCancelledLocked(job)) {
            //Job的stop流程,先不管它
            stopJobOnServiceContextLocked(job);
        }
    }
    
    //特殊机制
    if (backoffCount > 0 ||
            idleCount >= MIN_IDLE_COUNT ||
            connectivityCount >= MIN_CONNECTIVITY_COUNT ||
            chargingCount >= MIN_CHARGING_COUNT ||
            runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
        //debug log...
        for (int i=0; i<runnableJobs.size(); i++) {
            mPendingJobs.add(runnableJobs.get(i));
        }
    } else {
        //debug log...
    }
    //debug log...
}
复制代码

maybeQueueReadyJobsForExecutionLockedH方法会遍历当前列表中的所有JobStatus寻找已经满足执行条件的添加到runnableJobs列表中。 并不是满足了执行条件就会执行,JobSchedulerService有一个机制,它会以约束条件为维度进行计数,并为各个约束条件设置了一个阈值,只有超过阈值的Job才会添加到待执行列表mPendingJobs中。

1.6 JobHandler.maybeRunPendingJobsH方法
private void maybeRunPendingJobsH() {
    synchronized (mJobs) {
        if (mDeviceIdleMode) {
            //设备空闲状态禁止执行任何任务
            return;
        }
        Iterator<JobStatus> it = mPendingJobs.iterator();
        //debug log...
        while (it.hasNext()) {
            JobStatus nextPending = it.next();
            JobServiceContext availableContext = null;
            //这个for循环的工作时寻找可用的JobServiceContext
            for (int i=0; i<mActiveServices.size(); i++) {
                JobServiceContext jsc = mActiveServices.get(i);
                final JobStatus running = jsc.getRunningJob();
                if (running != null && running.matches(nextPending.getUid(),
                        nextPending.getJobId())) {
                    // Job已经在运行,则跳过
                    availableContext = null;
                    break;
                }
                
                if (jsc.isAvailable()) {
                    //找到了可用的JobServiceContext
                    availableContext = jsc;
                }
            }
            if (availableContext != null) {
                //debug log...
                //调用JobServiceContext.executeRunnableJob方法执行任务
                if (!availableContext.executeRunnableJob(nextPending)) {
                    //debug log...
                    //JobServiceContext执行任务失败,则将Job从JobStore中移除
                    mJobs.remove(nextPending);
                }
                //从mPendingJobs中移除对应的JobStatus
                it.remove();
            }
        }
    }
}
复制代码

该方法的主要逻辑很简单:为所有待启动任务寻找到可用的JobServiceContext,并执行任务(通过JobServiceContext.executeRunnableJob方法)。 那么JobServiceContext又是什么呢?我们知道一个Job就相当于一个Service,而JobServiceContext则负责与Service的对接工作。

1.7 JobServiceContext.enecuteRunnableJob
boolean executeRunnableJob(JobStatus job) {
    synchronized (mLock) {
        //...

        mRunningJob = job;
        final boolean isDeadlineExpired =
                job.hasDeadlineConstraint() &&
                        (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
        //创建一个JobParameters
        //JobParameters的第一个参数是一个IBinder对象
        //后续JobService可以拿到该对象与JobServiceContext
        //进行通信
        mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired);
        mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
        
        mVerb = VERB_BINDING;
        //发送超时消息
        scheduleOpTimeOut();
        final Intent intent = new Intent().setComponent(job.getServiceComponent());
        //创建并绑定服务
        boolean binding = mContext.bindServiceAsUser(intent, this,
                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
                new UserHandle(job.getUserId()));
        if (!binding) {
            //绑定失败的一些清理工作
            return false;
        }
        //...
        mAvailable = false;
        return true;
    }
}
复制代码

executeRunnableJob的主要工作就是通过bindServiceAsUser启动了JobService。由于JobServiceContext实现了ServiceConnection接口,后续JobServiceContext便可以与JobService进行通信。

有关服务绑定的细节我们就不赘述了,服务绑定成功之后会回调ServiceConnection.onServiceConnected方法。JobServiceContext.onServiceConnected方法的实现较简单,有兴趣大家可以自己分析,它会发送一个MSG_SERVICE_BOUND消息,最终触发JobServiceHandler.handleServiceBoundH方法(JobServiceHandlerJobServiceContext的内部类)。

1.8 JobServiceHandler.handleServiceBoundH方法
private void handleServiceBoundH() {
    //debug log...

    //异常检查
    if (mVerb != VERB_BINDING) {
        Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
                + VERB_STRINGS[mVerb]);
        closeAndCleanupJobH(false /* reschedule */);
        return;
    }
    //是否已取消
    if (mCancelled.get()) {
        if (DEBUG) {
            Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
                    + mRunningJob);
        }
        closeAndCleanupJobH(true /* reschedule */);
        return;
    }
    try {
        mVerb = VERB_STARTING;
        scheduleOpTimeOut();
        //service便是JobService.onBind方法返回的Binder对象
        //mParams是在executeRunnableJob方法中生成的
        service.startJob(mParams);
    } catch (RemoteException e) {
        Slog.e(TAG, "Error sending onStart message to '" +
                mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
    }
}
复制代码

handleServiceBoundH的主要逻辑页很简单,就是调用了JobService返回的Binder对象的startJob方法。这样JobService.onStartJob方法便触发了。

至此,JobScheduler的工作流程已经基本结束了。

1.9 总结

JobScheduler的工作主要由JobSchedulerServiceJobServiceContext以及各种StateController协同完成。 其中StateController负责监听设备的各种状态、更新JobStatus中对应字段的值,并通知JobSchedulerService. JobSchedulerService收到StateController的消息后,就开始检测Job的状态,如果有满足执行条件的Job则交给JobServiceContext执行。 而JobServiceContext则负责绑定服务等于JobService生命周期相关的事宜。

2 JobService销毁流程

JobService的销毁流程在前半部分会有两个分支:

  1. JobService的工作在onStartJob方法中就完成了,那么通过返回false就可以结束了(触发JobSchedulerContext.acknowledgeStartMessage方法)。(以下简称分支1)
  2. JobService执行的是一些耗时任务,那么则需要异步执行任务,那么就需要调用JobService.jobFinished方法来结束任务(触发JobSchedulerContext.jobFinished方法)。而此时onStartJob方法需要返回false。(以下简称分支2)

无论是acknowledgeStartMessage还是jobFinished,最终都会进入到JobServiceHandler.handleMessage方法的MSG_CALLBACK这个case中。

public void handleMessage(Message message) {
    switch (message.what) {
        //...
        case MSG_CALLBACK:
            //...

            if (mVerb == VERB_STARTING) {
                //acknowledgeStartMessage走这个分支
                final boolean workOngoing = message.arg2 == 1;
                handleStartedH(workOngoing);
            } else if (mVerb == VERB_EXECUTING ||
                    mVerb == VERB_STOPPING) {
                //jobFinished走这个分支
                final boolean reschedule = message.arg2 == 1;
                handleFinishedH(reschedule);
            } else {
                //...
            }
            break;
        //...
    }
}
复制代码

从流程上来讲,分支1只会走handleStartedH方法,而分支2会依次走handleStartedHhandleFinishedH方法。

2.1 JobServiceHandler.handleStartedH方法
private void handleStartedH(boolean workOngoing) {
    switch (mVerb) {
        case VERB_STARTING:
            mVerb = VERB_EXECUTING;
            if (!workOngoing) {
                //分支1场景workOngoing为false,立刻执行handleFinishedH
                handleFinishedH(false);
                return;
            }
            //取消流程
            if (mCancelled.get()) {
                //log...
                handleCancelH();
                return;
            }
            //分支2场景,workOngong为true,执行一个超时消息
            //后续JobService执行完毕调用jobFinished方法
            //还是会进入到handleFinishedH中。
            scheduleOpTimeOut();
            break;
        default:
            //log...
            return;
    }
}
复制代码
2.2 JobServiceHandler.handleFinishedH
private void handleFinishedH(boolean reschedule) {
    switch (mVerb) {
        case VERB_EXECUTING:
        case VERB_STOPPING:
            closeAndCleanupJobH(reschedule);
            break;
        default:
            //log...
    }
}
复制代码

直接调用了closeAndCleanupJobH方法

private void closeAndCleanupJobH(boolean reschedule) {
    final JobStatus completedJob = mRunningJob;
    synchronized (mLock) {
        //...
        mContext.unbindService(JobServiceContext.this);
        mWakeLock = null;
        mRunningJob = null;
        mParams = null;
        mVerb = -1;
        mCancelled.set(false);
        service = null;
        mAvailable = true;
    }
    removeOpTimeOut();
    removeMessages(MSG_CALLBACK);
    removeMessages(MSG_SERVICE_BOUND);
    removeMessages(MSG_CANCEL);
    removeMessages(MSG_SHUTDOWN_EXECUTION);
    mCompletedListener.onJobCompleted(completedJob, reschedule);
}
复制代码

主要做了这些事情:

  1. 解绑服务
  2. 重置一些变量的状态
  3. 移除消息队列中的所有消息
  4. 执行一个成功回调

mCompletedListener的本体就是JobSchedulerService

2.3 JobSchedulerService.onJobCompleted
public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
    //log...
    //stopTrackingJob的工作主要是讲JobStatus从JobStore
    //和各种StateController中移除
    //是与startTrackingJob相反的一个过程
    if (!stopTrackingJob(jobStatus)) {
        //log...
        return;
    }
    if (needsReschedule) {
        JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
        startTrackingJob(rescheduled);
    } else if (jobStatus.getJob().isPeriodic()) {
        JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
        startTrackingJob(rescheduledPeriodic);
    }
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
复制代码

主要工作:

  1. JobStatusJobStore和各种StateController中移除
  2. 如果需要重新执行,那么会生成一个新的JobStatus并调用startTrackingJob方法
  3. 不管是否要重新执行,都会发送MSG_CHECK_JOB消息,检查是否有满足条件的Job能够被执行。

以上,就是JobService的销毁流程了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值