JobScheduler之源码分析

文中的源代码版本为api23java

JobScheduler工做流程

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

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;
}
复制代码

该方法的逻辑仍是很是清晰的:app

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

其中二、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的构造函数中被初始化。ide

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逻辑比较简单,干了两件事情ui

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

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

咱们先来看看AppIdleController的构造函数spa

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的销毁流程了。

相关文章
相关标签/搜索