JSS 第 4 篇 - JobSchedulerService - cancel

基于 Android 7.1.1 源码分析

前提

接下来,我们来看看 JobServiceService 服务中和 cancel 相关的服务:
   
   
  1. // 取消指定设备用户的所有的 job!
  2. void cancelJobsForUser(int userHandle) {
  3. List<JobStatus> jobsForUser;
  4. synchronized (mLock) {
  5. jobsForUser = mJobs.getJobsByUser(userHandle);
  6. }
  7. for (int i=0; i<jobsForUser.size(); i++) {
  8. JobStatus toRemove = jobsForUser.get(i);
  9. cancelJobImpl(toRemove, null);
  10. }
  11. }
  12. // 取消指定 package 和指定 uid 的所有的 job!
  13. void cancelJobsForPackageAndUid(String pkgName, int uid) {
  14. List<JobStatus> jobsForUid;
  15. synchronized (mLock) {
  16. jobsForUid = mJobs.getJobsByUid(uid);
  17. }
  18. for (int i = jobsForUid.size() - 1; i >= 0; i--) {
  19. final JobStatus job = jobsForUid.get(i);
  20. if (job.getSourcePackageName().equals(pkgName)) {
  21. cancelJobImpl(job, null);
  22. }
  23. }
  24. }
  25. // 取消指定 uid 的应用程序的所有的 job!
  26. public void cancelJobsForUid(int uid, boolean forceAll) {
  27. List<JobStatus> jobsForUid;
  28. synchronized (mLock) {
  29. jobsForUid = mJobs.getJobsByUid(uid);
  30. }
  31. for (int i=0; i<jobsForUid.size(); i++) {
  32. JobStatus toRemove = jobsForUid.get(i);
  33. if (!forceAll) {
  34. String packageName = toRemove.getServiceComponent().getPackageName();
  35. try {
  36. if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
  37. != ActivityManager.APP_START_MODE_DISABLED) {
  38. continue;
  39. }
  40. } catch (RemoteException e) {
  41. }
  42. }
  43. cancelJobImpl(toRemove, null);
  44. }
  45. }
  46. // 取消指定 uid 的应用程序的 id 为 jobId 的 job!
  47. public void cancelJob(int uid, int jobId) {
  48. JobStatus toCancel;
  49. synchronized (mLock) {
  50. toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
  51. }
  52. if (toCancel != null) {
  53. cancelJobImpl(toCancel, null);
  54. }
  55. }
JobSchedulerService 提供了很多个接口来取消 job,但是他们都疯了似的调用了同一个方法,我们来继续看!

1 JSS.cancelJobImpl

同一个取消接口:
   
   
  1. private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
  2. if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
  3. // 停止监视这个 job,writeBack 为 true!
  4. stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
  5. synchronized (mLock) {
  6. // Remove from pending queue.
  7. // 从 mPendingJobs 中移除!
  8. if (mPendingJobs.remove(cancelled)) {
  9. mJobPackageTracker.noteNonpending(cancelled);
  10. }
  11. // Cancel if running.
  12. // 如果正在运行,就要停止这个 job
  13. stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
  14. reportActive();
  15. }
  16. }
这里我们一个一个来看:

1.1 JSS.stopTrackingJob

停止 stopTrackingJob 如下:
   
   
  1. private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
  2. boolean writeBack) {
  3. synchronized (mLock) {
  4. // Remove from store as well as controllers.
  5. // 先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件!
  6. final boolean removed = mJobs.remove(jobStatus, writeBack);
  7. if (removed && mReadyToRock) {
  8. for (int i=0; i<mControllers.size(); i++) {
  9. // 通知控制器,取消 track!
  10. StateController controller = mControllers.get(i);
  11. controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
  12. }
  13. }
  14. return removed;
  15. }
  16. }
先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件,通知控制器,取消 track!

1.2 JSS.stopJobOnServiceContextLocked

如果这个 job 是在运行中,就要取消它!
   
   
  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. }
每一个运行着的 job,都是和一个 JobServiceContext 绑定着的,这里是遍历所有的 JobServiceContext,找到要 cancel 的 job,调用 jobServiceContext 的 cancelExecutingJob 方法:

1.2.1 JSC.cancelExecutingJob

这个方法很简单,发送了 MSG_CANCEL 消息给 JobServiceHandler,reason 为 JobParameters.REASON_CANCELED
    
    
  1. void cancelExecutingJob(int reason) {
  2. mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
  3. }
我们继续看,进入 JobServiceHandler:

1.2.2 JSH.MSG_CANCEL

JobServiceHander 会处理 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.  
  8. ... ... ... ...
  9. case MSG_CANCEL: // 接受到了 MSG_CANCEL.
  10. if (mVerb == VERB_FINISHED) {
  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); // reason 为 JobParameters.REASON_CANCELED
  18. if (message.arg1 == JobParameters.REASON_PREEMPT) { // 不进入,因为这不是优先级取代!
  19. mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
  20. NO_PREFERRED_UID;
  21. }
  22. handleCancelH();
  23. break;
  24. case MSG_TIMEOUT:
  25. handleOpTimeoutH();
  26. break;
  27. case MSG_SHUTDOWN_EXECUTION:
  28. closeAndCleanupJobH(true /* needsReschedule */);
  29. break;
  30. default:
  31. Slog.e(TAG, "Unrecognised message: " + message);
  32. }
  33. }
  34.   ... ... ... ... ...
  35. }
调用 handleCancelH 方法来取消 Job:

1.2.3 JSH.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: // 如果 job 的状态是 VERB_BINDING 或者 VERB_STARTING,直接将 mCancelled 置为 true !
  18. case VERB_STARTING:
  19. mCancelled.set(true);
  20. break;
  21. case VERB_EXECUTING: // 如果 job 的状态是 VERB_EXECUTING,
  22. if (hasMessages(MSG_CALLBACK)) { // 判断客户端是否已经调用过 jobFinished 方法,有直接 return!
  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. }
我们来看看 sendStopMessageH 方法:

1.2.4 JSH.sendStopMessageH

   
   
  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) { // 如果 job 的状态已经不是 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; // 否则,job 状态设置为 VERB_STOPPING,
  14. scheduleOpTimeOut();
  15. service.stopJob(mParams); // 调用 jobService 的 stopJob,停止服务!
  16. } catch (RemoteException e) {
  17. Slog.e(TAG, "Error sending onStopJob to client.", e);
  18. closeAndCleanupJobH(false /* reschedule */);
  19. }
  20. }
这里会根据 mVerb 中存储的 job 的动作状态,来做相应的处理:
  • 如果 job 的状态已经不是 VERB_EXECUTING,那就清除资源,恢复初始化;
  • 否则,job 状态设置为 VERB_STOPPING,调用 jobService 的 stopJob,停止服务!
1.2.4.1 JobService.stopJob
通过 Binder 机制,停止 job:
  
  
  1. public abstract class JobService extends Service {
  2. ... ... ... ...
  3. private static final String TAG = "JobService";
  4. static final class JobInterface extends IJobService.Stub {
  5. final WeakReference<JobService> mService;
  6. JobInterface(JobService service) {
  7. mService = new WeakReference<>(service);
  8. }
  9. @Override
  10. public void startJob(JobParameters jobParams) throws RemoteException {
  11. JobService service = mService.get();
  12. if (service != null) {
  13. service.ensureHandler();
  14. Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
  15. m.sendToTarget();
  16. }
  17. }
  18. @Override
  19. public void stopJob(JobParameters jobParams) throws RemoteException {
  20. JobService service = mService.get();
  21. if (service != null) {
  22. service.ensureHandler(); // 确保 Handler 已经创建
  23. Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams); // 发送 MSG_STOP_JOB 到 JobHandler!
  24. m.sendToTarget();
  25. }
  26. }
  27. }
  28. ... ... ... ...
  29. }
进入 JobHandler 方法:
1.2.4.2 JobService.JobHandler
处理前面通过 Binder 发送过来的 MSG_STOP_JOB:
   
   
  1. class JobHandler extends Handler {
  2. JobHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message msg) {
  7. final JobParameters params = (JobParameters) msg.obj;
  8. switch (msg.what) {
  9. ... ... ... ...
  10. case MSG_STOP_JOB:
  11. try {
  12. // 这里是获得 JobService 的 onStopJob 的返回值!
  13. boolean ret = JobService.this.onStopJob(params);
  14. ackStopMessage(params, ret);
  15. } catch (Exception e) {
  16. Log.e(TAG, "Application unable to handle onStopJob.", e);
  17. throw new RuntimeException(e);
  18. }
  19. break;
  20. ... ... ... ...
  21. default:
  22. Log.e(TAG, "Unrecognised message received.");
  23. break;
  24. }
  25. }
  26. ... ... ... ...   
  27. private void ackStopMessage(JobParameters params, boolean reschedule) {
  28. final IJobCallback callback = params.getCallback();
  29. final int jobId = params.getJobId();
  30. if (callback != null) {
  31. try {
  32. // 将被停止的 job 的 id 和 onStopJob 的返回值发给 JobServiceContext!
  33. callback.acknowledgeStopMessage(jobId, reschedule);
  34. } catch(RemoteException e) {
  35. Log.e(TAG, "System unreachable for stopping job.");
  36. }
  37. } else {
  38. if (Log.isLoggable(TAG, Log.DEBUG)) {
  39. Log.d(TAG, "Attempting to ack a job that has already been processed.");
  40. }
  41. }
  42. }
  43. }
JobService 的 onStopJob 的返回值如果为 true,那么,就表示 JobService 需要重新拉起他!前面我们分析的时候,知道,callback 就是 JobServiceContext
1.2.4.3 JSC.acknowledgeStopMessage
接着发送 MSG_CALLBACK 给 JobServiceHandler:
   
   
  1. @Override
  2. public void acknowledgeStopMessage(int jobId, boolean reschedule) {
  3. if (!verifyCallingUid()) {
  4. return;
  5. }
  6. mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
  7. .sendToTarget();
  8. }
我们进入 JobServiceHandler 方法里面看看:
1.2.4.4 JSC.JobServiceHandler
   
   
  1. private class JobServiceHandler extends Handler {
  2. @Override
  3. public void handleMessage(Message message) {
  4. switch (message.what) {
  5. ... ... ... ...
  6. case MSG_CALLBACK:
  7. if (DEBUG) {
  8. Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
  9. + " v:" + VERB_STRINGS[mVerb]);
  10. }
  11. removeOpTimeOut();
  12. if (mVerb == VERB_STARTING) {
  13. final boolean workOngoing = message.arg2 == 1;
  14. handleStartedH(workOngoing);
  15. } else if (mVerb == VERB_EXECUTING ||
  16. mVerb == VERB_STOPPING) { // 进入这里!
  17. final boolean reschedule = message.arg2 == 1;
  18. handleFinishedH(reschedule);
  19. } else {
  20. if (DEBUG) {
  21. Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
  22. }
  23. }
  24. break;
  25. ... ... ... ...
  26. default:
  27. Slog.e(TAG, "Unrecognised message: " + message);
  28. }
  29. }
  30. ... ... ... ...
  31. /**
  32. * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
  33. * _STOPPING -> Successful finish, clean up and notify done.
  34. * _STARTING -> Error
  35. * _PENDING -> Error
  36. */
  37. private void handleFinishedH(boolean reschedule) {
  38. switch (mVerb) {
  39. case VERB_EXECUTING:
  40. case VERB_STOPPING:
  41. closeAndCleanupJobH(reschedule);
  42. break;
  43. default:
  44. Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
  45. "executed. Was " + VERB_STRINGS[mVerb] + ".");
  46. }
  47. }
  48. ... ... ... ...
  49. }
可以看到,最后会调用 JSH 的 closeAndCleanupJobH 方法:
1.2.4.5 JSH.closeAndCleanupJobH
如果 job 的状态已经不是 VERB_EXECUTING,那就清除资源,恢复初始化;这个方法很简单,就是将 JobServiceContext 中的属性值恢复初始化,表示没有任何 job 在运行!
    
    
  1. private void closeAndCleanupJobH(boolean reschedule) {
  2. final JobStatus completedJob;
  3. synchronized (mLock) {
  4. if (mVerb == VERB_FINISHED) {
  5. return;
  6. }
  7. completedJob = mRunningJob;
  8. mJobPackageTracker.noteInactive(completedJob);
  9. try {
  10. mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
  11. mRunningJob.getSourceUid());
  12. } catch (RemoteException e) {
  13. // Whatever.
  14. }
  15. if (mWakeLock != null) {
  16. mWakeLock.release();
  17. }
  18. mContext.unbindService(JobServiceContext.this); // 取消绑定 jobSerivce
  19. mWakeLock = null;
  20. mRunningJob = null; // mRunningJob 的值置为 null!
  21. mParams = null; // JobParamters 置为 null
  22. mVerb = VERB_FINISHED; // mVerb 的状态值为 VERB_FINISHED
  23. mCancelled.set(false); // mCancelled 置为 false
  24. service = null; // 应用的 JobService 代理对象置为 null
  25. mAvailable = true; // mAvailable 置为 true,表示这个 JobService 可以分配给其他 job
  26. }
  27. removeOpTimeOut();
  28. removeMessages(MSG_CALLBACK); // 清除处理过的 MSG
  29. removeMessages(MSG_SERVICE_BOUND);
  30. removeMessages(MSG_CANCEL);
  31. removeMessages(MSG_SHUTDOWN_EXECUTION);
  32. // 回调 JobSchedulerService 的 onJobCompleted 方法!
  33. mCompletedListener.onJobCompleted(completedJob, reschedule);
  34. }
恢复初始化,为下次做准备!
1.2.4.5.1 JSC.onServiceDisconnected
取消绑定 JobService 会调用 onServiceDisconnected
   
   
  1. /** If the client service crashes we reschedule this job and clean up. */
  2. @Override
  3. public void onServiceDisconnected(ComponentName name) {
  4. mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
  5. }
这里的会再回到 JobServiceHandler 中:
   
   
  1. case MSG_SHUTDOWN_EXECUTION:
  2. closeAndCleanupJobH(true /* needsReschedule */);
  3. break;
做第二次 clean,防止第一次不成功,其实第一次 clean 成功的话,mVerb 的值为 VERB_FINISHED,第二次 clean 会自动退出的!
1.2.4.5.2 JSS.onJobCompleted
我们进入 JSS 的 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,这里 stopTrackingJob 的返回值为 false!
  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. // 发送 MSG_CHECK_JOB_GREEDY,继续执行其他的 job,然后直接 return
  16. mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
  17. return;
  18. }
  19. // Note: there is a small window of time in here where, when rescheduling a job,
  20. // we will stop monitoring its content providers. This should be fixed by stopping
  21. // the old job after scheduling the new one, but since we have no lock held here
  22. // that may cause ordering problems if the app removes jobStatus while in here.
  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. mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
  32. }
这里首先调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除,因为之前已经移除过了,所以这个 stopTrackingJob 的返回值为 false
    
    
  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,注意,在之前已经移除过了,所以,这里的 removed 为 false!
  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. }
可以看出,对于用户主动 cancel 的任务,无论 onStopJob 的返回值是什么,都不会 reschedule 了!



























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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值