JSS 第 3 篇 - JobSchedulerService - schedule

基于 Android 7.1.1 源码分析

前言

我们先从基本的方法开始,也就是 schedule 方法,方法参数传递:
  • JobInfo job:需要 schedule 的任务!
  • int uId:调用方的 uid!
    
    
  1. public int schedule(JobInfo job, int uId) {
  2. return scheduleAsPackage(job, uId, null, -1, null);
  3. }
  4. public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId, String tag) {
  5. // 创建新的 jobStatus
  6. JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
  7. try {
  8. if (ActivityManagerNative.getDefault().getAppStartMode(uId,
  9. job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
  10. Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
  11. + " -- package not allowed to start");
  12. return JobScheduler.RESULT_FAILURE;
  13. }
  14. } catch (RemoteException e) {
  15. }
  16. if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
  17. JobStatus toCancel;
  18. synchronized (mLock) {
  19. // Jobs on behalf of others don't apply to the per-app job cap
  20. // 判断应用设置的任务数量是否超过上限:100
  21. if (ENFORCE_MAX_JOBS && packageName == null) {
  22. if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
  23. Slog.w(TAG, "Too many jobs for uid " + uId);
  24. throw new IllegalStateException("Apps may not schedule more than "
  25. + MAX_JOBS_PER_APP + " distinct jobs");
  26. }
  27. }
  28. // 如果同一个id,之前已经注册了一个任务,取消上一个任务
  29. toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
  30. if (toCancel != null) {
  31. cancelJobImpl(toCancel, jobStatus);
  32. }
  33. // 开始追踪该任务
  34. startTrackingJob(jobStatus, toCancel);
  35. }
  36. // 向 system_server 进程的主线程发送 message:MSG_CHECK_JOB
  37. mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
  38. return JobScheduler.RESULT_SUCCESS;
  39. }
我们是使用 schedule 方法将我们需要的任务注册到系统中的,传入的参数主要是:
  • JobInfo:封装任务的基本信息!
  • uId:调用者的 uid!

我们接着来看,先调用 JobStatus 的 createFromJobInfo ,创建 JobStatus:

1 JobStatus.createFromJobInfo

参数传递:job, uId, null, -1, null,
    
    
  1. /**
  2. * Create a newly scheduled job.
  3. * @param callingUid Uid of the package that scheduled this job.
  4. * @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
  5. * the calling package is the source.
  6. * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
  7. */
  8. public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
  9. int sourceUserId, String tag) {
  10. final long elapsedNow = SystemClock.elapsedRealtime(); // 从开机到现在的时间
  11. final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
  12. // 判断这个任务是否是周期性的
  13. if (job.isPeriodic()) {
  14. latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
  15. earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
  16. } else {
  17. earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
  18. elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
  19. latestRunTimeElapsedMillis = job.hasLateConstraint() ?
  20. elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
  21. }
  22. return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, tag, 0,
  23. earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
  24. }
这里通过 JobInfo 创建了对应的 JobStatus 对象,参数传递:
job, uId, null, -1, null,0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis。
    
    
  1. private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
  2. int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis,
  3. long latestRunTimeElapsedMillis) {
  4. this.job = job;
  5. this.callingUid = callingUid;
  6. int tempSourceUid = -1;
  7. if (sourceUserId != -1 && sourcePackageName != null) {
  8. try {
  9. tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
  10. sourceUserId);
  11. } catch (RemoteException ex) {
  12. // Can't happen, PackageManager runs in the same process.
  13. }
  14. }
  15. if (tempSourceUid == -1) {
  16. this.sourceUid = callingUid;
  17. this.sourceUserId = UserHandle.getUserId(callingUid);
  18. this.sourcePackageName = job.getService().getPackageName();
  19. this.sourceTag = null;
  20. } else {
  21. this.sourceUid = tempSourceUid;
  22. this.sourceUserId = sourceUserId;
  23. this.sourcePackageName = sourcePackageName;
  24. this.sourceTag = tag;
  25. }
  26. this.batteryName = this.sourceTag != null
  27. ? this.sourceTag + ":" + job.getService().getPackageName()
  28. : job.getService().flattenToShortString();
  29. this.tag = "*job*/" + this.batteryName;
  30. this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
  31. this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
  32. this.numFailures = numFailures;
  33. int requiredConstraints = 0;
  34. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY) {
  35. requiredConstraints |= CONSTRAINT_CONNECTIVITY;
  36. }
  37. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED) {
  38. requiredConstraints |= CONSTRAINT_UNMETERED;
  39. }
  40. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_NOT_ROAMING) {
  41. requiredConstraints |= CONSTRAINT_NOT_ROAMING;
  42. }
  43. if (job.isRequireCharging()) {
  44. requiredConstraints |= CONSTRAINT_CHARGING;
  45. }
  46. if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
  47. requiredConstraints |= CONSTRAINT_TIMING_DELAY;
  48. }
  49. if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
  50. requiredConstraints |= CONSTRAINT_DEADLINE;
  51. }
  52. if (job.isRequireDeviceIdle()) {
  53. requiredConstraints |= CONSTRAINT_IDLE;
  54. }
  55. if (job.getTriggerContentUris() != null) {
  56. requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
  57. }
  58. this.requiredConstraints = requiredConstraints;
  59. }
我们先看到这里!

2 JobStore.countJobsForUid

这里是统计 uid 对用的应用有多少个任务:
    
    
  1. public int countJobsForUid(int uid) {
  2. return mJobSet.countJobsForUid(uid);
  3. }
JobStore 有一个集合,用来存放所有的 Job!
    
    
  1. final JobSet mJobSet;
我们来看看这个 JobSet
    
    
  1. static class JobSet {
  2. // Key is the getUid() originator of the jobs in each sheaf
  3. // mJobs 的 key 是应用的 uid!
  4. private SparseArray<ArraySet<JobStatus>> mJobs;
  5. public JobSet() {
  6. mJobs = new SparseArray<ArraySet<JobStatus>>();
  7. }
  8. ... ... ... ...
  9. // We only want to count the jobs that this uid has scheduled on its own
  10. // behalf, not those that the app has scheduled on someone else's behalf.
  11. // 统计 uid 对应的应用注册的任务数!
  12. public int countJobsForUid(int uid) {
  13. int total = 0;
  14. ArraySet<JobStatus> jobs = mJobs.get(uid);
  15. if (jobs != null) {
  16. for (int i = jobs.size() - 1; i >= 0; i--) {
  17. JobStatus job = jobs.valueAt(i);
  18. if (job.getUid() == job.getSourceUid()) {
  19. total++;
  20. }
  21. }
  22. }
  23. return total;
  24. }
  25. ... ... ... ...
  26. }
JobSet 有很多的其他方法:getJobsByXXX,add,remove,getXXX,等等的方法,这里我们先不看!

3 JobStore.getJobByUidAndJobId

接着是根据 uid 和 jobId,来获得一个任务,这里的目的是判断是否之前已经注册过了一个相同的任务:
    
    
  1. /**
  2. * @param uid Uid of the requesting app.
  3. * @param jobId Job id, specified at schedule-time.
  4. * @return the JobStatus that matches the provided uId and jobId, or null if none found.
  5. */
  6. public JobStatus getJobByUidAndJobId(int uid, int jobId) {
  7. return mJobSet.get(uid, jobId);
  8. }
还是调用的是 JobSet.get 方法:
    
    
  1. public JobStatus get(int uid, int jobId) {
  2. ArraySet<JobStatus> jobs = mJobs.get(uid);
  3. if (jobs != null) {
  4. for (int i = jobs.size() - 1; i >= 0; i--) {
  5. JobStatus job = jobs.valueAt(i);
  6. if (job.getJobId() == jobId) {
  7. return job;
  8. }
  9. }
  10. }
  11. return null;
  12. }
这个很简单,不详细说了!

4 JSS.cancelJobImpl

如果之前已经注册过一个任务了,需要先取消掉之前的任务!
    
    
  1. private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
  2. if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
  3. // 停止追踪任务!
  4. stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
  5. synchronized (mLock) {
  6. // Remove from pending queue.
  7. // 如果这个任务在等待队列中,移除它。
  8. if (mPendingJobs.remove(cancelled)) {
  9. mJobPackageTracker.noteNonpending(cancelled);
  10. }
  11. // Cancel if running.
  12. // 如果正在运行,取消这个任务!
  13. stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
  14. reportActive();
  15. }
  16. }
这里主要做的是:停止对这个任务的监视,同时,将这个任务移除等待队列,如果这个任务正在运行,那么就要取消它,我们一个一个来看!

4.1 JSS.stopTrackingJob

我们先来看第一个方法,参数传递:
  • JobStatus jobStatus:要被取消的任务;
  • JobStatus incomingJob:本次要注册的任务;
  • boolean writeBack:true;
    
    
  1. /**
  2. * Called when we want to remove a JobStatus object that we've finished executing. Returns the
  3. * object removed.
  4. */
  5. private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
  6. boolean writeBack) {
  7. synchronized (mLock) {
  8. // Remove from store as well as controllers.
  9. final boolean removed = mJobs.remove(jobStatus, writeBack);
  10. if (removed && mReadyToRock) {
  11. for (int i=0; i<mControllers.size(); i++) {
  12. StateController controller = mControllers.get(i);
  13. controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
  14. }
  15. }
  16. return removed;
  17. }
  18. }
这里的 mJobs 是 JobStore 对象!

4.1.1 JobStore.remove

我们来看看这个移除操作:
    
    
  1. public boolean remove(JobStatus jobStatus, boolean writeBack) {
  2. // 先从 mJobSet 中移除要删除的 JobStatus!
  3. boolean removed = mJobSet.remove(jobStatus);
  4. if (!removed) {
  5. if (DEBUG) {
  6. Slog.d(TAG, "Couldn't remove job: didn't exist: " + jobStatus);
  7. }
  8. return false;
  9. }
  10. // 如果 writeBack 是 true,并且 jobStatus 是 isPersisted 的,那就要更新 Jobs.xml 文件!
  11. if (writeBack && jobStatus.isPersisted()) {
  12. maybeWriteStatusToDiskAsync();
  13. }
  14. return removed;
  15. }
从 JobSet 集合中移除 Job,并且,如果需要写入操作,并且这个 Job 是设备重启后仍然需要保留的,那就要调用 remove 方法,将更新后的 JobSet 写入到 system/job/jobs.xml,因为所有 Persisted 为 true 的 Job ,都是会被写入到这个文件中去的,用于重启恢复!!

我们来看 maybeWriteStatusToDiskAsync 方法:
    
    
  1. /**
  2. * Every time the state changes we write all the jobs in one swath, instead of trying to
  3. * track incremental changes.
  4. * @return Whether the operation was successful. This will only fail for e.g. if the system is
  5. * low on storage. If this happens, we continue as normal
  6. */
  7. private void maybeWriteStatusToDiskAsync() {
  8. mDirtyOperations++;
  9. if (mDirtyOperations >= MAX_OPS_BEFORE_WRITE) {
  10. if (DEBUG) {
  11. Slog.v(TAG, "Writing jobs to disk.");
  12. }
  13. mIoHandler.post(new WriteJobsMapToDiskRunnable());
  14. }
  15. }
  16. ... ... ... ... ... ...
  17.  
  18. /**
  19. * Runnable that writes {@link #mJobSet} out to xml.
  20. * NOTE: This Runnable locks on mLock
  21. */
  22. private class WriteJobsMapToDiskRunnable implements Runnable {
  23. @Override
  24. public void run() {
  25. final long startElapsed = SystemClock.elapsedRealtime();
  26. final List<JobStatus> storeCopy = new ArrayList<JobStatus>();
  27. synchronized (mLock) {
  28. // Clone the jobs so we can release the lock before writing.
  29. // 这里是将 mJobSet 拷贝一份到 storeCopy 中。
  30. mJobSet.forEachJob(new JobStatusFunctor() {
  31. @Override
  32. public void process(JobStatus job) {
  33. if (job.isPersisted()) {
  34. storeCopy.add(new JobStatus(job));
  35. }
  36. }
  37. });
  38. }
  39. // 将更新后的拷贝写入 jobs.xml。
  40. writeJobsMapImpl(storeCopy);
  41. if (JobSchedulerService.DEBUG) {
  42. Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime()
  43. - startElapsed) + "ms");
  44. }
  45. }
  46. ... ... ... ...
  47.  
  48. }
我们接着看!

4.1.2 SC.maybeStopTrackingJobLocked

接着,就是遍历控制器集合,让控制器停止对该任务的监视,参数传递:
  • JobStatus jobStatus:要被删除的 Job!
  • JobStatus incomingJob:同一个 uid,同一个 jobId 的要被注册执行的 Job!
  • boolean forUpdate:false
    
    
  1. /**
  2. * Remove task - this will happen if the task is cancelled, completed, etc.
  3. */
  4. public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
  5. boolean forUpdate);
StateController 是一个抽象类,具体的实现,我们在 JobSchedulerService 的构造器中有看到:
    
    
  1. mControllers.add(ConnectivityController.get(this));
  2. mControllers.add(TimeController.get(this));
  3. mControllers.add(IdleController.get(this));
  4. mControllers.add(BatteryController.get(this));
  5. mControllers.add(AppIdleController.get(this));
  6. mControllers.add(ContentObserverController.get(this));
  7. mControllers.add(DeviceIdleJobsController.get(this));
这里有很多的控制器:
4.1.2.1 ConnectivityController
我们先来看看 ConnectivityController 类的方法:
    
    
  1. @Override
  2. public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
  3. boolean forUpdate) {
  4. if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()
  5. || jobStatus.hasNotRoamingConstraint()) {
  6. mTrackedJobs.remove(jobStatus);
  7. }
  8. }
其他控制器的方法很类似的哦!

4.2 JobPackageTracker.noteNonpending

这个就是这样的
    
    
  1. public void noteNonpending(JobStatus job) {
  2. final long now = SystemClock.uptimeMillis();
  3. mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now);
  4. rebatchIfNeeded(now);
  5. }
这里我们先不看!

4.3 JSS.stopJobOnServiceContextLocked

如果 Job 有在运行,那就停止它,参数传递:cancelled, JobParameters.REASON_CANCELED
    
    
  1. private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
  2. for (int i=0; i<mActiveServices.size(); i++) {
  3. JobServiceContext jsc = mActiveServices.get(i);
  4. final JobStatus executing = jsc.getRunningJob();
  5. if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
  6. jsc.cancelExecutingJob(reason);
  7. return true;
  8. }
  9. }
  10. return false;
  11. }
这里调用了 JobServiceContext 的 cancelExecutingJob 这个方法:

4.3.1 JobServiceContext.cancelExecutingJob

我们进入 JobServiceContext 文件中来看看:
    
    
  1. /** Called externally when a job that was scheduled for execution should be cancelled. */
  2. void cancelExecutingJob(int reason) {
  3. mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
  4. }
其中:mCallbackHandler = new JobServiceHandler(looper),这里的 looper 是 system_server 的主线程的 looper,这个在第二篇初始化和启动就可以看到!

这里我们向 JobServiceHandler 发送了一个 MSG_CANCEL 的消息,我们去看看:
    
    
  1. private class JobServiceHandler extends Handler {
  2. @Override
  3. public void handleMessage(Message message) {
  4. switch (message.what) {
  5.  
  6. ... ... ... ...
  7. case MSG_CANCEL: // 收到 MSG_CANCEL 的消息!
  8. // 处理出入的参数:reason
  9. if (mVerb == VERB_FINISHED) {
  10. if (DEBUG) {
  11. Slog.d(TAG, "Trying to process cancel for torn-down context, ignoring.");
  12. }
  13. return;
  14. }
  15. mParams.setStopReason(message.arg1); // 保存停止的原因!
  16. if (message.arg1 == JobParameters.REASON_PREEMPT) {
  17. mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
  18. NO_PREFERRED_UID;
  19. }
  20. handleCancelH();
  21. break;
  22. case MSG_TIMEOUT:
  23. handleOpTimeoutH();
  24. break;
  25. case MSG_SHUTDOWN_EXECUTION:
  26. closeAndCleanupJobH(true /* needsReschedule */);
  27. break;
  28. default:
  29. Slog.e(TAG, "Unrecognised message: " + message);
  30. }
  31. }
  32. ... ... ... ...
  33. }
我们进入到 handleCancelH 方法中来看看:
    
    
  1. /**
  2. * A job can be in various states when a cancel request comes in:
  3. * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
  4. * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
  5. * _STARTING -> Mark as cancelled and wait for
  6. * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
  7. * _EXECUTING -> call {@link #sendStopMessageH}}, but only if there are no callbacks
  8. * in the message queue.
  9. * _ENDING -> No point in doing anything here, so we ignore.
  10. */
  11. private void handleCancelH() {
  12. if (JobSchedulerService.DEBUG) {
  13. Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
  14. + VERB_STRINGS[mVerb]);
  15. }
  16. switch (mVerb) {
  17. case VERB_BINDING:
  18. case VERB_STARTING:
  19. mCancelled.set(true);
  20. break;
  21. case VERB_EXECUTING:
  22. if (hasMessages(MSG_CALLBACK)) {
  23. // If the client has called jobFinished, ignore this cancel.
  24. return;
  25. }
  26. sendStopMessageH();
  27. break;
  28. case VERB_STOPPING:
  29. // Nada.
  30. break;
  31. default:
  32. Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
  33. break;
  34. }
  35. }
mVerb 使用来保存 Job 的状态的,一个 Job 在 JobServiceContext 中会有如下的几种状态:
  • VERB_BINDING 
  • _STARTING
  • _EXECUTING
  • _ENDING
这里我先不看!

5 JSS.startTrackingJob

接下来,就是开始 track 任务,参数传递:
  • JobStatus jobStatus:本次注册的 job!
  • JobStatus lastJob:同一个 uid,同一个 jobId,上次注册的已经被取消的任务,可以为 null!
    
    
  1. /**
  2. * Called when we have a job status object that we need to insert in our
  3. * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
  4. * about.
  5. */
  6. private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
  7. synchronized (mLock) {
  8. // 添加到 JobStore 中!
  9. final boolean update = mJobs.add(jobStatus);
  10. if (mReadyToRock) {
  11. for (int i = 0; i < mControllers.size(); i++) {
  12. StateController controller = mControllers.get(i);
  13. if (update) {
  14. controller.maybeStopTrackingJobLocked(jobStatus, null, true);
  15. }
  16. controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
  17. }
  18. }
  19. }
  20. }
第一步,可以看到,先将这一次要注册执行的任务,加入到 JobStore 中:

5.1 JobStore.add

这里是将要好注册执行的 Job 添加到 JobStore 中,这里和上面有些类似:
    
    
  1. * Add a job to the master list, persisting it if necessary. If the JobStatus already exists,
  2. * it will be replaced.
  3. * @param jobStatus Job to add.
  4. * @return Whether or not an equivalent JobStatus was replaced by this operation.
  5. */
  6. public boolean add(JobStatus jobStatus) {
  7. boolean replaced = mJobSet.remove(jobStatus);
  8. mJobSet.add(jobStatus); // 添加到 JobSets 集合中!
  9. if (jobStatus.isPersisted()) {
  10. maybeWriteStatusToDiskAsync();
  11. }
  12. if (DEBUG) {
  13. Slog.d(TAG, "Added job status to store: " + jobStatus);
  14. }
  15. return replaced;
  16. }
如果这个 Job 是 isPersisted 的,那就需要更新 Jobs.xml 文件!

5.2 SC.maybeStopTrackingJobLocked

这里先要停止 track 这个任务,参数传递:
  • JobStatus jobStatus:要被注册执行的新的 Job!
  • JobStatus incomingJob:null
  • boolean forUpdate:true
    
    
  1. /**
  2. * Remove task - this will happen if the task is cancelled, completed, etc.
  3. */
  4. public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
  5. boolean forUpdate);
接着看!

5.3 SC.maybeStartTrackingJobLocked

接着,调用这个方法,来开始 track 任务,方法参数:
  • JobStatus jobStatus:这次要注册的任务
  • JobStatus lastJob:同一个 uid,同一个 jobId 已经被取消掉的上一个任务,可以为 null!
    
    
  1. /**
  2. * Implement the logic here to decide whether a job should be tracked by this controller.
  3. * This logic is put here so the JobManager can be completely agnostic of Controller logic.
  4. * Also called when updating a task, so implementing controllers have to be aware of
  5. * preexisting tasks.
  6. */
  7. public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
这里同样是一个抽象接口,具体的实现是子类:

5.3.1 ConnectivityController

让我们来看看 ConnectivityController 对象的这个方法:
    
    
  1. @Override
  2. public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
  3. if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()
  4. || jobStatus.hasNotRoamingConstraint()) {
  5. updateConstraintsSatisfied(jobStatus);
  6. // 将这个 Job 添加到 ConnectivityController 的跟踪列表中!
  7. mTrackedJobs.add(jobStatus);
  8. }
  9. }
首先,判断 Job 是否有设置和 Connectivity 网络相关的属性:
  • jobStatus.hasConnectivityConstraint()
  • jobStatus.hasUnmeteredConstraint()
  • jobStatus.hasNotRoamingConstraint()
这里我们先不看!

6 JSS.JobHandler

接着,就是向 JobHandler 发送了 MSG_CHECK_JOB 的消息,我们来看看:
    
    
  1. private class JobHandler extends Handler {
  2. public JobHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message message) {
  7. synchronized (mLock) {
  8. if (!mReadyToRock) {
  9. return;
  10. }
  11. }
  12. switch (message.what) {
  13. case MSG_JOB_EXPIRED:
  14. ... ... ... ...
  15. break;
  16. case MSG_CHECK_JOB: // 接收到 MSG_CHECK_JOB 的消息!
  17. synchronized (mLock) {
  18. if (mReportedActive) { // 为 true,表示 JSS 通知了设备管理器,自己处于活跃状态!
  19. // if jobs are currently being run, queue all ready jobs for execution.
  20. queueReadyJobsForExecutionLockedH();
  21. } else {
  22. // Check the list of jobs and run some of them if we feel inclined.
  23. maybeQueueReadyJobsForExecutionLockedH();
  24. }
  25. }
  26. break;
  27. case MSG_CHECK_JOB_GREEDY:
  28. synchronized (mLock) {
  29. queueReadyJobsForExecutionLockedH();
  30. }
  31. break;
  32. case MSG_STOP_JOB:
  33. cancelJobImpl((JobStatus)message.obj, null);
  34. break;
  35. }
  36. // 处理 mPendingJobs 中的任务!
  37. maybeRunPendingJobsH();
  38. // Don't remove JOB_EXPIRED in case one came along while processing the queue.
  39. removeMessages(MSG_CHECK_JOB);
  40. }
  41. ... ... ... ... ...
  42. }
接着,收到了 MSG_CHECK_JOB 的消息,开始遍历队列,执行准备好的任务!

6.1 queueReadyJobsForExecutionLockedH

下面是这个方法的代码:
    
    
  1. /**
  2. * Run through list of jobs and execute all possible - at least one is expired so we do
  3. * as many as we can.
  4. */
  5. private void queueReadyJobsForExecutionLockedH() {
  6. if (DEBUG) {
  7. Slog.d(TAG, "queuing all ready jobs for execution:");
  8. }
  9. noteJobsNonpending(mPendingJobs);
  10. mPendingJobs.clear();
  11. mJobs.forEachJob(mReadyQueueFunctor);
  12. mReadyQueueFunctor.postProcess();
  13. if (DEBUG) {
  14. final int queuedJobs = mPendingJobs.size();
  15. if (queuedJobs == 0) {
  16. Slog.d(TAG, "No jobs pending.");
  17. } else {
  18. Slog.d(TAG, queuedJobs + " jobs queued.");
  19. }
  20. }
  21. }
啦啦啦啦啦啦

6.2 maybeQueueReadyJobsForExecutionLockedH

这个方法的代码:
    
    
  1. private void maybeQueueReadyJobsForExecutionLockedH() {
  2. if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
  3. noteJobsNonpending(mPendingJobs);
  4. // 清除 mPendingJobs 集合!
  5. mPendingJobs.clear();
  6. // 将准备好的 Job 加入到 JobHandler 内部的 runnableJobs 集合中。
  7. mJobs.forEachJob(mMaybeQueueFunctor);
  8. // 将 runnableJobs 加入到 mPendingJobs 集合中!
  9. mMaybeQueueFunctor.postProcess();
  10. }
首先清除了:mPendingJobs 集合!
接着,这里传入了 mMaybeQueueFunctor 对象!
    
    
  1. public void forEachJob(JobStatusFunctor functor) {
  2. mJobSet.forEachJob(functor);
  3. }
进入了 JobSet:
    
    
  1. public void forEachJob(JobStatusFunctor functor) {
  2. for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) {
  3. ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex);
  4. for (int i = jobs.size() - 1; i >= 0; i--) {
  5. // 对每个 uid 的应用程序的 Job,执行下面操作:
  6. functor.process(jobs.valueAt(i));
  7. }
  8. }
  9. }
可以看出,这对所有的 Job,都调用了 MaybeReadyJobQueueFunctor 的 process 方法:
    
    
  1. class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
  2. int chargingCount;
  3. int idleCount;
  4. int backoffCount;
  5. int connectivityCount;
  6. int contentCount;
  7. List<JobStatus> runnableJobs; // 符合条件的即将运行的 Job
  8. public MaybeReadyJobQueueFunctor() {
  9. reset();
  10. }
  11. // Functor method invoked for each job via JobStore.forEachJob()
  12. @Override
  13. public void process(JobStatus job) {
  14. // 判断这个 Job 是否是准备了!
  15. if (isReadyToBeExecutedLocked(job)) {
  16. try {
  17. if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
  18. job.getJob().getService().getPackageName())
  19. == ActivityManager.APP_START_MODE_DISABLED) {
  20. Slog.w(TAG, "Aborting job " + job.getUid() + ":"
  21. + job.getJob().toString() + " -- package not allowed to start");
  22. mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
  23. return;
  24. }
  25. } catch (RemoteException e) {
  26. }
  27. if (job.getNumFailures() > 0) {
  28. backoffCount++;
  29. }
  30. if (job.hasIdleConstraint()) {
  31. idleCount++;
  32. }
  33. if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
  34. || job.hasNotRoamingConstraint()) {
  35. connectivityCount++;
  36. }
  37. if (job.hasChargingConstraint()) {
  38. chargingCount++;
  39. }
  40. if (job.hasContentTriggerConstraint()) {
  41. contentCount++;
  42. }
  43. if (runnableJobs == null) {
  44. runnableJobs = new ArrayList<>();
  45. }
  46. // 将满足条件的 Job,先加入到 runnableJobs 集合!
  47. runnableJobs.add(job);
  48. } else if (areJobConstraintsNotSatisfiedLocked(job)) {
  49. stopJobOnServiceContextLocked(job,
  50. JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
  51. }
  52. }
这里调用了 isReadyToBeExecutedLocked 方法来判断,这个 job 是否已经准备好了:
     
     
  1. /**
  2. * Criteria for moving a job into the pending queue:
  3. * - It's ready.
  4. * - It's not pending.
  5. * - It's not already running on a JSC.
  6. * - The user that requested the job is running.
  7. * - The component is enabled and runnable.
  8. */
  9. private boolean isReadyToBeExecutedLocked(JobStatus job) {
  10. final boolean jobReady = job.isReady();
  11. final boolean jobPending = mPendingJobs.contains(job);
  12. final boolean jobActive = isCurrentlyActiveLocked(job);
  13. final int userId = job.getUserId();
  14. final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
  15. final boolean componentPresent;
  16. try {
  17. componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
  18. job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
  19. userId) != null);
  20. } catch (RemoteException e) {
  21. throw e.rethrowAsRuntimeException();
  22. }
  23. if (DEBUG) {
  24. Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
  25. + " ready=" + jobReady + " pending=" + jobPending
  26. + " active=" + jobActive + " userStarted=" + userStarted
  27. + " componentPresent=" + componentPresent);
  28. }
  29. return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
  30. }
可以看出,一个准备好的 Job 要满足这些条件:
*- It's ready.
*- It's not pending.
*- It's not already running on a JSC.
*- The user that requested the job is running.
*- The component is enabled and runnable.

最后,调用 MaybeQueueFunctor.postProcess 方法:
    
    
  1. public void postProcess() {
  2. if (backoffCount > 0 ||
  3. idleCount >= mConstants.MIN_IDLE_COUNT ||
  4. connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
  5. chargingCount >= mConstants.MIN_CHARGING_COUNT ||
  6. contentCount >= mConstants.MIN_CONTENT_COUNT ||
  7. (runnableJobs != null
  8. && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
  9. if (DEBUG) {
  10. Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
  11. }
  12. noteJobsPending(runnableJobs);
  13. // 将 runnableJobs 加入到 mPendingJobs 集合中!
  14. mPendingJobs.addAll(runnableJobs);
  15. } else {
  16. if (DEBUG) {
  17. Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
  18. }
  19. }
  20. // Be ready for next time
  21. reset();
  22. }
该功能:
  • 先将 JobStore 的 JobSet 中满足条件的 job 对应的 JobStatus 加入 runnableJobs 队列;
  • 再将 runnableJobs 中满足触发条件的 JobStatus 加入到 mPendingJobs 队列;

6.3 maybeRunPendingJobsH

接着,就是处理 mPendingJobs 中的 Job:
    
    
  1. /**
  2. * Reconcile jobs in the pending queue against available execution contexts.
  3. * A controller can force a job into the pending queue even if it's already running, but
  4. * here is where we decide whether to actually execute it.
  5. */
  6. private void maybeRunPendingJobsH() {
  7. synchronized (mLock) {
  8. if (DEBUG) {
  9. Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
  10. }
  11. assignJobsToContextsLocked();
  12. reportActive();
  13. }
  14. }
结合就是调用 JSS 的 assignJobsToContextLocked 方法:

6.3.1 JSS.assignJobsToContextsLocked

这个方法其实从名字上就可以看出,就是把 Job 分配给 JobServiceContext 方法,我们去那个方法里面看一下:
    
    
  1. /**
  2. * Takes jobs from pending queue and runs them on available contexts.
  3. * If no contexts are available, preempts lower priority jobs to
  4. * run higher priority ones.
  5. * Lock on mJobs before calling this function.
  6. */
  7. private void assignJobsToContextsLocked() {
  8. if (DEBUG) {
  9. Slog.d(TAG, printPendingQueue());
  10. }
  11.  // 根据当前系统的内存级别,设定最大的活跃 Job 数,保存到 mMaxActiveJobs!
  12. int memLevel;
  13. try {
  14. memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
  15. } catch (RemoteException e) {
  16. memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
  17. }
  18. switch (memLevel) {
  19. case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
  20. mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
  21. break;
  22. case ProcessStats.ADJ_MEM_FACTOR_LOW:
  23. mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
  24. break;
  25. case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
  26. mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
  27. break;
  28. default:
  29. mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
  30. break;
  31. }
  32. JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; // 大小为 16,和 mActiveServices 相对应!
  33. boolean[] act = mTmpAssignAct;
  34. int[] preferredUidForContext = mTmpAssignPreferredUidForContext; // 大小为 16,和 mActiveServices 相对应!
  35. int numActive = 0;
  36. int numForeground = 0;
  37. for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
  38. final JobServiceContext js = mActiveServices.get(i);
  39. final JobStatus status = js.getRunningJob();
  40. // 初始化 contextIdToJobMap,里面保存当前运行着的 Job,和空闲的用于增加的 Job 位置!
  41. // 然后,分别计算活跃 job 和前台 job 的个数!
  42. if ((contextIdToJobMap[i] = status) != null) {
  43. numActive++;
  44. if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
  45. numForeground++;
  46. }
  47. }
  48. act[i] = false;
  49. preferredUidForContext[i] = js.getPreferredUid();
  50. }
  51. if (DEBUG) {
  52. Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
  53. }
  54. // 遍历 mPendingJos 任务集合,开始做准备工作!
  55. for (int i=0; i<mPendingJobs.size(); i++) {
  56. JobStatus nextPending = mPendingJobs.get(i);
  57. // If job is already running, go to next job.
  58. // 判断当前的 Job 是不是在 contextIdToJobMap 中了,即他是不是已经在运行了!
  59. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
  60. if (jobRunningContext != -1) {
  61. continue;
  62. }
  63. // 计算 Job 的优先级!
  64. final int priority = evaluateJobPriorityLocked(nextPending);
  65. nextPending.lastEvaluatedPriority = priority;
  66. // Find a context for nextPending. The context should be available OR
  67. // it should have lowest priority among all running jobs
  68. // (sharing the same Uid as nextPending)
  69. // 遍历 contextIdToJobMap
  70. // 给这个即将被执行的 Job 找一个合适的 context,至少要满足两个中的一个要求:1、可利用;2、优先级最低!
  71. int minPriority = Integer.MAX_VALUE;
  72. int minPriorityContextId = -1;
  73. for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
  74. JobStatus job = contextIdToJobMap[j];
  75. int preferredUid = preferredUidForContext[j];
  76. if (job == null) {
  77. if ((numActive < mMaxActiveJobs ||
  78. (priority >= JobInfo.PRIORITY_TOP_APP &&
  79. numForeground < mConstants.FG_JOB_COUNT)) &&
  80. (preferredUid == nextPending.getUid() ||
  81. preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
  82. // This slot is free, and we haven't yet hit the limit on
  83. // concurrent jobs... we can just throw the job in to here.
  84. // 如果 context 原有的 job 为空,并且满足以下条件:
  85. // 活跃 job 数小于最大活跃数,或者即将执行的 job 的优先级不低于 PRIORITY_TOP_APP,并且前台 job 数小于 4,那么这个 context 是合适的!
  86. minPriorityContextId = j;
  87. break;
  88. }
  89. // No job on this context, but nextPending can't run here because
  90. // the context has a preferred Uid or we have reached the limit on
  91. // concurrent jobs.
  92. continue;
  93. }
  94. if (job.getUid() != nextPending.getUid()) { // 如果 context 原有的 job 不为空,且 uid 和即将执行的 job 不一样,那么这个 context 不合适!
  95. continue;
  96. }
  97. if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) { // 如果 context 原有的 job 不为空,且 uid 相同,但优先级不低于即将执行的 job,那么这个 context 不合适!
  98. continue;
  99. }
  100. if (minPriority > nextPending.lastEvaluatedPriority) { // 如果 context 原有的 job 不为空,且 uid 相同,且优先级低于即将执行的 job,那么这个 context 合适!
  101. minPriority = nextPending.lastEvaluatedPriority;
  102. minPriorityContextId = j;
  103. }
  104. }
  105. if (minPriorityContextId != -1) { // 找到了合适的 context 的下标!
  106. // 将这个要被执行的 Job 放入合适的 Context 中!
  107. contextIdToJobMap[minPriorityContextId] = nextPending;
  108. // 对应的 act 位置为 true,表示可以运行!
  109. act[minPriorityContextId] = true;
  110. numActive++; //
  111. if (priority >= JobInfo.PRIORITY_TOP_APP) {
  112. numForeground++;
  113. }
  114. }
  115. }
  116. if (DEBUG) {
  117. Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
  118. }
  119. mJobPackageTracker.noteConcurrency(numActive, numForeground);
  120. // 执行任务!
  121. for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
  122. boolean preservePreferredUid = false;
  123. if (act[i]) { // art 为 true 的位置,对应的就是上面添加的即将执行的 Job
  124. JobStatus js = mActiveServices.get(i).getRunningJob();
  125. if (js != null) {
  126. if (DEBUG) {
  127. Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
  128. }
  129. // preferredUid will be set to uid of currently running job.
  130. // 如果这个 context 已经在运行了一个优先级低的 job,那就要取消它!
  131. mActiveServices.get(i).preemptExecutingJob();
  132. preservePreferredUid = true;
  133. } else {
  134. // 从 contextIdToJobMap 中获得即将执行的 Job!
  135. final JobStatus pendingJob = contextIdToJobMap[i];
  136. if (DEBUG) {
  137. Slog.d(TAG, "About to run job on context "
  138. + String.valueOf(i) + ", job: " + pendingJob);
  139. }
  140. // 通知控制器!
  141. for (int ic=0; ic<mControllers.size(); ic++) {
  142. mControllers.get(ic).prepareForExecutionLocked(pendingJob);
  143. }
  144. // 使用对应的 JobServiceContext 来执行 Job
  145. if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
  146. Slog.d(TAG, "Error executing " + pendingJob);
  147. }
  148. // Job 已经开始执行了,从 mPendingJobs 中移除这个 Job!
  149. if (mPendingJobs.remove(pendingJob)) {
  150. mJobPackageTracker.noteNonpending(pendingJob);
  151. }
  152. }
  153. }
  154. if (!preservePreferredUid) {
  155. mActiveServices.get(i).clearPreferredUid();
  156. }
  157. }
  158. }
接着我们继续来看:
6.3.1.1 JSS.findJobContextIdFromMap
这个方法的作用很简单,就是判断 job 是否已经在 map 集合中了!
    
    
  1. int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
  2. for (int i=0; i<map.length; i++) {
  3. if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
  4. return i;
  5. }
  6. }
  7. return -1;
  8. }
6.3.1.2 JSS.evaluateJobPriorityLocked
这个方法是为 job 计算优先级:
    
    
  1. private int evaluateJobPriorityLocked(JobStatus job) {
  2. int priority = job.getPriority();
  3. if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
  4. return adjustJobPriority(priority, job);
  5. }
  6. int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
  7. if (override != 0) {
  8. return adjustJobPriority(override, job);
  9. }
  10. return adjustJobPriority(priority, job);
  11. }
接着:
6.3.1.3 JSC.preemptExecutingJob
这里调用了 JobServiceContext 的 preemptExecutingJob 方法来取消优先级低的任务,之前有说过,如果 Context 已经在执行一个 job,并且这个 job 和即将被执行的 job 属于同一个 apk,那么要取消优先级低的!
    
    
  1. void preemptExecutingJob() {
  2. Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
  3. // 参数为 JobParameters.REASON_PREEMPT,表示要取代!
  4. m.arg1 = JobParameters.REASON_PREEMPT;
  5. m.sendToTarget();
  6. }
这个消息会发给 JSC 内部的 JobServiceHandler 来处理:
JobServiceHandler.MSG_CANCEL
我们接着来看:
    
    
  1. private class JobServiceHandler extends Handler {
  2. JobServiceHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message message) {
  7. switch (message.what) {
  8. ... ... ... ...
  9. case MSG_CANCEL:
  10. if (mVerb == VERB_FINISHED) { // 这个 mVerb 表示的是,这个 Context 的 job 的当前状态!
  11. if (DEBUG) {
  12. Slog.d(TAG,
  13. "Trying to process cancel for torn-down context, ignoring.");
  14. }
  15. return;
  16. }
  17. mParams.setStopReason(message.arg1);
  18. if (message.arg1 == JobParameters.REASON_PREEMPT) {
  19. mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
  20. NO_PREFERRED_UID;
  21. }
  22. // 执行取消操作!
  23. handleCancelH();
  24. break;
  25. case MSG_TIMEOUT:
  26. handleOpTimeoutH();
  27. break;
  28. case MSG_SHUTDOWN_EXECUTION:
  29. closeAndCleanupJobH(true /* needsReschedule */);
  30. break;
  31. default:
  32. Slog.e(TAG, "Unrecognised message: " + message);
  33. }
  34. }
  35. ... ... ... ... ... ...
  36. private void handleCancelH() {
  37. if (JobSchedulerService.DEBUG) {
  38. Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
  39. + VERB_STRINGS[mVerb]);
  40. }
  41. switch (mVerb) { // 这里因为是取消的是这个 context 里面正在执行的 job,所以 mVerb 的值为 VERB_EXECUTING!
  42. case VERB_BINDING:
  43. case VERB_STARTING:
  44. mCancelled.set(true);
  45. break;
  46. case VERB_EXECUTING:
  47. if (hasMessages(MSG_CALLBACK)) { // 如果 client 已经调用了 jobFinished 方法结束了 job,那就 return!
  48. // If the client has called jobFinished, ignore this cancel.
  49. return;
  50. }
  51. sendStopMessageH(); // 否则,就再次发送停止的消息!
  52. break;
  53. case VERB_STOPPING:
  54. // Nada.
  55. break;
  56. default:
  57. Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
  58. break;
  59. }
  60. }
  61. ... ... ... ...
  62. }
如果有优先级低的 job 正在运行,那就 stop job,这时 mVerb 会从: VERB_EXECUTING ->  VERB_STOPPING.
    
    
  1. /**
  2. * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
  3. * VERB_STOPPING.
  4. */
  5. private void sendStopMessageH() {
  6. removeOpTimeOut();
  7. if (mVerb != VERB_EXECUTING) {
  8. Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
  9. closeAndCleanupJobH(false /* reschedule */);
  10. return;
  11. }
  12. try {
  13. mVerb = VERB_STOPPING; // mVerb 状态变为 VERB_STOPPING!
  14. scheduleOpTimeOut();
  15. service.stopJob(mParams); // 这里就调用了 client 的 JobService 的
  16. } catch (RemoteException e) {
  17. Slog.e(TAG, "Error sending onStopJob to client.", e);
  18. closeAndCleanupJobH(false /* reschedule */);
  19. }
  20. }
这里的 service 是一个 IJobService 对象,对应的是一个 .aidl 文件,表示的是 Binder 服务端,代码定义位于:JobService 中。
JobService
注意:这里对于 Binder 机制来说:应用程序 client 的 JobService 所在进程是 Binder 服务端,JobServiceContext 所在的进程 system_server 是 Binder 客户端!也就是说,应用程序定义的 JobService 是被 JobSerivceContext 来 bind 的,所以,你会发现,你无法 override JobService 的 onbind 方法!
    
    
  1. static final class JobInterface extends IJobService.Stub {
  2. final WeakReference<JobService> mService;
  3. JobInterface(JobService service) {
  4. mService = new WeakReference<>(service);
  5. }
  6. @Override
  7. public void startJob(JobParameters jobParams) throws RemoteException {
  8. JobService service = mService.get();
  9. if (service != null) {
  10. service.ensureHandler();
  11. Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
  12. m.sendToTarget();
  13. }
  14. }
  15. @Override
  16. public void stopJob(JobParameters jobParams) throws RemoteException { // 通过 binder 机制来来调用指定应用的 JobService 的 stopJob 方法!
  17. JobService service = mService.get();
  18. if (service != null) {
  19. service.ensureHandler();
  20. // 发送到 JobService 内部的 JobHandler 对象中!
  21. Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
  22. m.sendToTarget();
  23. }
  24. }
  25. }
这里发送了 MSG_STOP_JOB 消息给 JobService.JobHandler,我们去 JobHandler 内部去看看:
JobHandler
JobHandler 位于应用程序的主线程:
    
    
  1. /**
  2. * Runs on application's main thread - callbacks are meant to offboard work to some other
  3. * (app-specified) mechanism.
  4. * @hide
  5. */
  6. class JobHandler extends Handler {
  7. JobHandler(Looper looper) {
  8. super(looper);
  9. }
  10. @Override
  11. public void handleMessage(Message msg) {
  12. // 这里获得传递过来的 JobParameters
  13. final JobParameters params = (JobParameters) msg.obj;
  14. switch (msg.what) {
  15. ... ... ...
  16. case MSG_STOP_JOB:
  17. try {
  18. // 调用 JobService onStopJob 方法,把参数传递过去,这里是不是很熟悉,就不多说!
  19. // onStopJob 会返回 true / false, true 表示还会 reschedule 这个 job!
  20. boolean ret = JobService.this.onStopJob(params);
  21. ackStopMessage(params, ret);
  22. } catch (Exception e) {
  23. Log.e(TAG, "Application unable to handle onStopJob.", e);
  24. throw new RuntimeException(e);
  25. }
  26. break;
  27. ... ... ... ...
  28. default:
  29. Log.e(TAG, "Unrecognised message received.");
  30. break;
  31. }
  32. }
  33. ... ... ... ... ...
  34. private void ackStopMessage(JobParameters params, boolean reschedule) {
  35. // 这里获得了 IJobCallback 对象,这里显示是 Binder 机制,服务端是 JobServiceContext
  36. final IJobCallback callback = params.getCallback();
  37. final int jobId = params.getJobId();
  38. if (callback != null) {
  39. try {
  40. // 发送消息给 JobServiceContext
  41. callback.acknowledgeStopMessage(jobId, reschedule);
  42. } catch(RemoteException e) {
  43. Log.e(TAG, "System unreachable for stopping job.");
  44. }
  45. } else {
  46. if (Log.isLoggable(TAG, Log.DEBUG)) {
  47. Log.d(TAG, "Attempting to ack a job that has already been processed.");
  48. }
  49. }
  50. }
  51. }
这里的 IJobCallback 又是使用了 Binder 机制, Binder 客户端是应用程序的 JobService 所在进程,Binder 服务端是 JobServiceContext 所在的进程,最后调用的是  JobServiceContext. acknowledgeStopMessage 方法:
JobServiceContext
通过 Binder 机制,将回调信息发回给 JobServiceContext
    
    
  1. @Override
  2. public void acknowledgeStopMessage(int jobId, boolean reschedule) {
  3. if (!verifyCallingUid()) {
  4. return;
  5. }
  6. // 发送 MSG_CALLBACK 给 JobServiceHandler
  7. mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
  8. .sendToTarget();
  9. }
然后又会发送 MSG_CALLBACK 给 JobServiceContext.JobServiceHandler,这里我们只看关键代码:
JobServiceHandler
JobServiceHandler 同样的也是位于主线程:
    
    
  1. case MSG_CALLBACK:
  2. if (DEBUG) {
  3. Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
  4. + " v:" + VERB_STRINGS[mVerb]);
  5. }
  6. removeOpTimeOut();
  7. if (mVerb == VERB_STARTING) {
  8. final boolean workOngoing = message.arg2 == 1;
  9. handleStartedH(workOngoing);
  10. } else if (mVerb == VERB_EXECUTING ||
  11. mVerb == VERB_STOPPING) { // 从前面跟代码,可以看出 mVerb 的值为 VERB_STOPPING.
  12. final boolean reschedule = message.arg2 == 1;
  13. handleFinishedH(reschedule);
  14. } else {
  15. if (DEBUG) {
  16. Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
  17. }
  18. }
  19. break;
接着,调用 JobServiceContext 的 handleFinishedH 方法:
    
    
  1. private void handleFinishedH(boolean reschedule) {
  2. switch (mVerb) {
  3. case VERB_EXECUTING:
  4. case VERB_STOPPING:
  5. closeAndCleanupJobH(reschedule); // 调用了 closeAndCleanupJobH,reschedul
  6. break;
  7. default:
  8. Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
  9. "executed. Was " + VERB_STRINGS[mVerb] + ".");
  10. }
  11. }
接着进入 closeAndCleanupJobH 方法:
    
    
  1. /**
  2. * The provided job has finished, either by calling
  3. * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
  4. * or from acknowledging the stop message we sent. Either way, we're done tracking it and
  5. * we want to clean up internally.
  6. */
  7. private void closeAndCleanupJobH(boolean reschedule) {
  8. final JobStatus completedJob;
  9. synchronized (mLock) {
  10. if (mVerb == VERB_FINISHED) {
  11. return;
  12. }
  13. completedJob = mRunningJob;
  14. mJobPackageTracker.noteInactive(completedJob);
  15. try {
  16. mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
  17. mRunningJob.getSourceUid());
  18. } catch (RemoteException e) {
  19. // Whatever.
  20. }
  21. if (mWakeLock != null) {
  22. mWakeLock.release();
  23. }
  24. mContext.unbindService(JobServiceContext.this); // 取消绑定 JobService
  25. mWakeLock = null;
  26. mRunningJob = null;
  27. mParams = null;
  28. mVerb = VERB_FINISHED;
  29. mCancelled.set(false);
  30. service = null;
  31. mAvailable = true;
  32. }
  33. removeOpTimeOut(); // 移除已经处理的消息
  34. removeMessages(MSG_CALLBACK);
  35. removeMessages(MSG_SERVICE_BOUND);
  36. removeMessages(MSG_CANCEL);
  37. removeMessages(MSG_SHUTDOWN_EXECUTION);
  38. // 调用了 mCompletedListener 的 onJobCompleted 方法!
  39. mCompletedListener.onJobCompleted(completedJob, reschedule);
  40. }
  41. }
这里 mCompletedListener 大家去看 JobServiceContext 的初始化,也就是第一篇,其实就是 JobSchedulerService.this:
JobSchedulerService.onJobCompleted
   
   
  1. @Override
  2. public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
  3. if (DEBUG) {
  4. Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
  5. }
  6. // Do not write back immediately if this is a periodic job. The job may get lost if system
  7. // shuts down before it is added back.
  8. // 停止 track 这个 job!
  9. if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
  10. if (DEBUG) {
  11. Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
  12. }
  13. // We still want to check for jobs to execute, because this job may have
  14. // scheduled a new job under the same job id, and now we can run it.
  15. mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
  16. return;
  17. }
  18. // Note: there is a small window of time in here where, when rescheduling a job,
  19. // we will stop monitoring its content providers. This should be fixed by stopping
  20. // the old job after scheduling the new one, but since we have no lock held here
  21. // that may cause ordering problems if the app removes jobStatus while in here.
  22. // 重新 schedule 这个 job
  23. if (needsReschedule) {
  24. JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
  25. startTrackingJob(rescheduled, jobStatus);
  26. } else if (jobStatus.getJob().isPeriodic()) {
  27. JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
  28. startTrackingJob(rescheduledPeriodic, jobStatus);
  29. }
  30. reportActive();
  31. // 发送 MSG_CHECK_JOB_GREEDY 给 JobSchedulerService.JobHandler
  32. mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
  33. }
这里首先调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除:
    
    
  1. /**
  2. * Called when we want to remove a JobStatus object that we've finished executing. Returns the
  3. * object removed.
  4. */
  5. private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
  6. boolean writeBack) {
  7. synchronized (mLock) {
  8. // Remove from store as well as controllers.
  9. // 从 JobStore 中移除这个 job,如果 writeback 为 true,还要更新本地的 job.xml 文件!
  10. final boolean removed = mJobs.remove(jobStatus, writeBack);
  11. if (removed && mReadyToRock) {
  12. for (int i=0; i<mControllers.size(); i++) {
  13. StateController controller = mControllers.get(i);
  14. // 从 Controller 的跟踪队列中移除!
  15. controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
  16. }
  17. }
  18. return removed;
  19. }
  20. }
最后是发 MSG_CHECK_JOB_GREEDY 给 JobHandler:
JobHandler
   
   
  1. private class JobHandler extends Handler {
  2. @Override
  3. public void handleMessage(Message message) {
  4. synchronized (mLock) {
  5. if (!mReadyToRock) {
  6. return;
  7. }
  8. }
  9. switch (message.what) {
  10. ... ... ... ...
  11. case MSG_CHECK_JOB_GREEDY:
  12. synchronized (mLock) {
  13. // 作用和 maybeQueueReadyJobsForExecutionLockedH 一样的都是更新 mPendingJobs 集合!
  14. queueReadyJobsForExecutionLockedH();
  15. }
  16. break;
  17. ... ... ... ...
  18. }
  19. // 再次执行 mPendingJobs 中的 job
  20. maybeRunPendingJobsH();
  21. // Don't remove JOB_EXPIRED in case one came along while processing the queue.
  22. removeMessages(MSG_CHECK_JOB);
  23. }
  24. ... ... ... ...
  25. }
最后继续 maybeRunPendingJobsH,这里又回到了第 6 节了,就不多说了!
6.3.1.4 SC.prepareForExecutionLocked
这个方法其实很简单,就是一个抽象类的方法:
    
    
  1. /**
  2. * Optionally implement logic here to prepare the job to be executed.
  3. */
  4. public void prepareForExecutionLocked(JobStatus jobStatus) {
  5. }
表示通知 StateController,做好准备,具体实现是在 Controller 中,我们先看看 ConnectivityController,其他类似:

6.3.1.5 JSC.executeRunnableJob
这里就是调用 JobSchedulerContext 方法来执行 Job:
    
    
  1. /**
  2. * Give a job to this context for execution. Callers must first check {@link #getRunningJob()}
  3. * and ensure it is null to make sure this is a valid context.
  4. * @param job The status of the job that we are going to run.
  5. * @return True if the job is valid and is running. False if the job cannot be executed.
  6. */
  7. boolean executeRunnableJob(JobStatus job) {
  8. synchronized (mLock) {
  9. if (!mAvailable) {
  10. Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
  11. return false;
  12. }
  13. mPreferredUid = NO_PREFERRED_UID;
  14. // 保存到 mRunningJob 中,
  15. mRunningJob = job;
  16. final boolean isDeadlineExpired =
  17. job.hasDeadlineConstraint() &&
  18. (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
  19. Uri[] triggeredUris = null;
  20. if (job.changedUris != null) {
  21. triggeredUris = new Uri[job.changedUris.size()];
  22. job.changedUris.toArray(triggeredUris);
  23. }
  24. String[] triggeredAuthorities = null;
  25. if (job.changedAuthorities != null) {
  26. triggeredAuthorities = new String[job.changedAuthorities.size()];
  27. job.changedAuthorities.toArray(triggeredAuthorities);
  28. }
  29. // 创建 job 需要的 JobParamters
  30. mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired,
  31. triggeredUris, triggeredAuthorities);
  32. mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
  33.  // mVerb 的值变为 VERB_BINDING!
  34. mVerb = VERB_BINDING;
  35. scheduleOpTimeOut();
  36. // 这里很关键,bind 应用程序中注册的 JobService!
  37. final Intent intent = new Intent().setComponent(job.getServiceComponent());
  38. boolean binding = mContext.bindServiceAsUser(intent, this,
  39. Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
  40. new UserHandle(job.getUserId()));
  41. if (!binding) {
  42. if (DEBUG) {
  43. Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
  44. }
  45. mRunningJob = null;
  46. mParams = null;
  47. mExecutionStartTimeElapsed = 0L;
  48. mVerb = VERB_FINISHED;
  49. removeOpTimeOut();
  50. return false;
  51. }
  52. // 记录信息!
  53. try {
  54. mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
  55. } catch (RemoteException e) {
  56. // Whatever.
  57. }
  58. mJobPackageTracker.noteActive(job);
  59. mAvailable = false;
  60. return true;
  61. }
  62. }
这便是由 system_server 进程的主线程来执行 bind Service 的方式来拉起的进程,当服务启动后回调到发起端的 onServiceConnected。

关于 bindService 这里不讨论,另开一贴!
6.3.1.5.1 JSC.onServiceConnected
bind 成功后,作为 Binder 机制的客户端,JobServiceContext 的
    
    
  1. @Override
  2. public void onServiceConnected(ComponentName name, IBinder service) {
  3. JobStatus runningJob;
  4. synchronized (mLock) {
  5. // This isn't strictly necessary b/c the JobServiceHandler is running on the main
  6. // looper and at this point we can't get any binder callbacks from the client. Better
  7. // safe than sorry.
  8. runningJob = mRunningJob;
  9. }
  10. // 异常检测
  11. if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
  12. mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
  13. return;
  14. }
  15. // 获得了应用程序的 JobService 的代理对象!
  16. this.service = IJobService.Stub.asInterface(service);
  17. final PowerManager pm =
  18. (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
  19. PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  20. runningJob.getTag());
  21. wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
  22. wl.setReferenceCounted(false);
  23. wl.acquire();
  24. synchronized (mLock) {
  25. // We use a new wakelock instance per job. In rare cases there is a race between
  26. // teardown following job completion/cancellation and new job service spin-up
  27. // such that if we simply assign mWakeLock to be the new instance, we orphan
  28. // the currently-live lock instead of cleanly replacing it. Watch for this and
  29. // explicitly fast-forward the release if we're in that situation.
  30. if (mWakeLock != null) {
  31. Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
  32. + " tag=" + mWakeLock.getTag());
  33. mWakeLock.release();
  34. }
  35. mWakeLock = wl;
  36. }
  37. // 发送 MSG_SERVICE_BOUND 给 JobServiceHandler 中!
  38. mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
  39. }
可以看到,这里会发送消息到 JobServiceHandler 中:
6.3.1.5.2 JSC.JobServiceHandler
JobServiceHandler 方法也是在主线程中!
    
    
  1. private class JobServiceHandler extends Handler {
  2. JobServiceHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message message) {
  7. switch (message.what) {
  8. case MSG_SERVICE_BOUND: // 绑定成功的消息!
  9. removeOpTimeOut();
  10. handleServiceBoundH();
  11. break;
  12. ... ... ... ...
  13. case MSG_SHUTDOWN_EXECUTION: // 绑定是出现异常的消息!
  14. closeAndCleanupJobH(true /* needsReschedule */);
  15. break;
  16. default:
  17. Slog.e(TAG, "Unrecognised message: " + message);
  18. }
  19. }
  20.  
  21.   ... ... ... ...
  22.  
  23. }
接着我们来分别看一下:
6.3.1.5.2.1 JSH.MSG_SERVICE_BOUND
收到这个消息后,调用 handleServiceBoundH 方法:
    
    
  1. /** Start the job on the service. */
  2. private void handleServiceBoundH() {
  3. if (DEBUG) {
  4. Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
  5. }
  6. if (mVerb != VERB_BINDING) { // 状态异常
  7. Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
  8. + VERB_STRINGS[mVerb]);
  9. closeAndCleanupJobH(false /* reschedule */);
  10. return;
  11. }
  12. if (mCancelled.get()) { // 如果发现 JobService 对应的 job 被取消了,那就 return!
  13. if (DEBUG) {
  14. Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
  15. + mRunningJob);
  16. }
  17. closeAndCleanupJobH(true /* reschedule */);
  18. return;
  19. }
  20. try {
  21. // mVerb 的值设置为 VERB_STARTING!
  22. mVerb = VERB_STARTING;
  23. scheduleOpTimeOut();
  24. // 这里调用了 JobService 的 startJob 方法
  25. service.startJob(mParams);
  26. } catch (RemoteException e) {
  27. Slog.e(TAG, "Error sending onStart message to '" +
  28. mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
  29. }
  30. }
这里就不细看了!

7 总结

通过分析,我们可以看到 JobSchedulerService 相关类的关系:






















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值