上篇文章JobScheduler 详解一讲述了 JobScheduler 的服务启动,本篇文章将继续上篇文章,以 TimeController 为例,讲述 JobScheduler 的 schedule 和 cancel 流程。仍旧使用上篇文章给出的 demo:
private static ComponentName sService = new ComponentName("com.example.mi.myjobtest", MyJobService.class.getName()); public static void schedule(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder(JOB_ID, sService) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//网络条件,默认值NETWORK_TYPE_NONE .setPeriodic(DAY_TIME)//任务执行周期 .setPersisted(true)//设备重启后是否继续执行 .setRequiresCharging(true)//设置是否需要充电 .build(); js.schedule(job); }先给出 JobScheduler 的 schedule 流程时序图如下:
点击查看大图JobSchedulerService.java
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; }JobStatus.java
/** Create a newly scheduled job. */ public JobStatus(JobInfo job, int uId) { this(job, uId, 0); final long elapsedNow = SystemClock.elapsedRealtime(); if (job.isPeriodic()) {// 执行过 setPeriodic() earliestRunTimeElapsedMillis = elapsedNow; latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis(); } else { earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; latestRunTimeElapsedMillis = job.hasLateConstraint() ? elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; } }JobSchedulerService.java
public void cancelJob(int uid, int jobId) { JobStatus toCancel; synchronized (mJobs) { // 根据 uid 和 jobId 来查找相应的 JobStatus toCancel = mJobs.getJobByUidAndJobId(uid, jobId); } if (toCancel != null) { cancelJobImpl(toCancel); } }JobSchedulerService.java
private void startTrackingJob(JobStatus jobStatus) { boolean update; boolean rocking; synchronized (mJobs) { // 把 jobStatus 添加到 mJobs 中 update = mJobs.add(jobStatus); // mReadyToRock 在 phase600 是变为 true rocking = mReadyToRock; } if (rocking) { for (int i=0; i<mControllers.size(); i++) { StateController controller = mControllers.get(i); if (update) { controller.maybeStopTrackingJob(jobStatus); } // 检查是否需要将该任务添加到相应的追踪控制器 controller.maybeStartTrackingJob(jobStatus); } } }执行完此步之后,通过 JobHandler 发送 MSG_CHECK_JOB 消息,接下来进入其 handleMessage
JobSchedulerService.java :: JobHandler
private class JobHandler extends Handler { @Override public void handleMessage(Message message) { ... switch (message.what) { case MSG_JOB_EXPIRED: ... break; case MSG_CHECK_JOB: synchronized (mJobs) { // Check the list of jobs and run some of them if we feel inclined. maybeQueueReadyJobsForExecutionLockedH(); } break; case MSG_STOP_JOB: cancelJobImpl((JobStatus)message.obj); break; } maybeRunPendingJobsH(); removeMessages(MSG_CHECK_JOB); } }JobSchedulerService.java :: JobHandler
private void maybeQueueReadyJobsForExecutionLockedH() { int chargingCount = 0; int idleCount = 0; int backoffCount = 0; int connectivityCount = 0; List<JobStatus> runnableJobs = new ArrayList<JobStatus>(); ArraySet<JobStatus> jobs = mJobs.getJobs(); for (int i=0; i<jobs.size(); i++) { JobStatus job = jobs.valueAt(i); if (isReadyToBeExecutedLocked(job)) { if (job.getNumFailures() > 0) { backoffCount++; } if (job.hasIdleConstraint()) { idleCount++; } if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { connectivityCount++; } if (job.hasChargingConstraint()) { chargingCount++; } //将所有 ready 的 jobs 加入 runnableJobs 队列 runnableJobs.add(job); } else if (isReadyToBeCancelledLocked(job)) { stopJobOnServiceContextLocked(job); } } if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT || chargingCount >= MIN_CHARGING_COUNT || runnableJobs.size() >= MIN_READY_JOBS_COUNT) { for (int i=0; i<runnableJobs.size(); i++) { //加入到 mPendingJobs 队列 mPendingJobs.add(runnableJobs.get(i)); } } }该方法实现的功能是: * 先将所有 ready 的 jobs 加入 runnableJobs 队列中 * 如果满足发送条件,那么将 runnableJobs 中的所有 jobStatus 加入到 mPendingJobs 中
JobSchedulerService.java :: JobHandler
private boolean isReadyToBeExecutedLocked(JobStatus job) { final boolean jobReady = job.isReady(); final boolean jobPending = mPendingJobs.contains(job); final boolean jobActive = isCurrentlyActiveLocked(job); final boolean userRunning = mStartedUsers.contains(job.getUserId()); return userRunning && jobReady && !jobPending && !jobActive; }只有当用户是运行的、job 满足触发条件、job 不在 mPendingJobs 队列中、job 不在运行中时才会返回 true
JobSchedulerService.java :: JobHandler
private void maybeRunPendingJobsH() { synchronized (mJobs) { ... Iterator<JobStatus> it = mPendingJobs.iterator(); while (it.hasNext()) { JobStatus nextPending = it.next(); JobServiceContext availableContext = null; 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())) { // Already running this job for this uId, skip. availableContext = null; break; } if (jsc.isAvailable()) { availableContext = jsc; } } if (availableContext != null) { if (!availableContext.executeRunnableJob(nextPending)) { // 执行出错,在 mJobs 中删除 mJobs.remove(nextPending); } it.remove(); } } } }此段代码的作用是找一个 available 的 JobServiceContext 来运行 nextPending job (如果 job 已经在运行中了则跳过)
JobServiceContext.java
boolean executeRunnableJob(JobStatus job) { synchronized (mLock) { mRunningJob = job; // 是否达到 Deadline final boolean isDeadlineExpired = job.hasDeadlineConstraint() && (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime()); 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) { mRunningJob = null; mParams = null; mExecutionStartTimeElapsed = 0L; mVerb = VERB_FINISHED; removeOpTimeOut(); return false; } try { mBatteryStats.noteJobStart(job.getName(), job.getUid()); } catch (RemoteException e) { } // 正在执行 job 置为 false mAvailable = false; return true; } }这是 system_server 进程的主线程来执行 bind Service,从而拉起目标进程,服务启动后会回调到发起端的 onServiceConnected
JobServiceContext.java
public void onServiceConnected(ComponentName name, IBinder service) { JobStatus runningJob; synchronized (mLock) { // 即为上面执行的 job runningJob = mRunningJob; } if (runningJob == null || !name.equals(runningJob.getServiceComponent())) { mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); return; } // 获取远程 JobService 的代理端 // 后续会调用 service.startJob this.service = IJobService.Stub.asInterface(service); ... mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); }this.service是指获取远程 IJobService 的代理端,mCallbackHandler 运行在 system_server 的主线程
JobServiceContext.java :: JobServiceHandler
private class JobServiceHandler extends Handler { public void handleMessage(Message message) { switch (message.what) { case MSG_SERVICE_BOUND: removeOpTimeOut(); handleServiceBoundH(); break; ... } } }JobServiceContext.java :: JobServiceHandler
private void handleServiceBoundH() { if (mVerb != VERB_BINDING) { closeAndCleanupJobH(false /* reschedule */); return; } if (mCancelled.get()) { closeAndCleanupJobH(true /* reschedule */); return; } try { mVerb = VERB_STARTING; scheduleOpTimeOut(); service.startJob(mParams); } catch (RemoteException e) { ... } }此处的service是由【小节1.5.1】所赋值,是指 app 端 IJobService 的代理类。经过 binder call 回到app进程
JobService.java
public abstract class JobService extends Service { /** Binder for this service. */ IJobService mBinder = new IJobService.Stub() { @Override public void startJob(JobParameters jobParams) { ensureHandler(); Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams); m.sendToTarget(); } @Override public void stopJob(JobParameters jobParams) { ensureHandler(); Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams); m.sendToTarget(); } }; }由于 JobService 运行在 app 端所在进程,那么此处的 mHandler 便是指app进程的主线程。
JobService.java :: JobHandler
class JobHandler extends Handler { ... public void handleMessage(Message msg) { final JobParameters params = (JobParameters) msg.obj; switch (msg.what) { case MSG_EXECUTE_JOB: boolean workOngoing = JobService.this.onStartJob(params); ackStartMessage(params, workOngoing); break; case MSG_STOP_JOB: ... case MSG_JOB_FINISHED:... default: break; } } }JobSchedulerService.java ::JobSchedulerStub
final class JobSchedulerStub extends IJobScheduler.Stub { public void cancel(int jobId) throws RemoteException { final int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { JobSchedulerService.this.cancelJob(uid, jobId); } finally { Binder.restoreCallingIdentity(ident); } } @Override public void cancelAll() throws RemoteException { final int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { JobSchedulerService.this.cancelJobsForUid(uid, true); } finally { Binder.restoreCallingIdentity(ident); } } }JobSchedulerService.java
public void cancelJob(int uid, int jobId) { JobStatus toCancel; synchronized (mJobs) { toCancel = mJobs.getJobByUidAndJobId(uid, jobId); } if (toCancel != null) { cancelJobImpl(toCancel); } } public void cancelJobsForUid(int uid, boolean forceAll) { List<JobStatus> jobsForUid; synchronized (mJobs) { jobsForUid = mJobs.getJobsByUid(uid); } for (int i=0; i<jobsForUid.size(); i++) { JobStatus toRemove = jobsForUid.get(i); if (!forceAll) { String packageName = toRemove.getServiceComponent().getPackageName(); try { if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName) != ActivityManager.APP_START_MODE_DISABLED) { continue; } } catch (RemoteException e) { } } cancelJobImpl(toRemove); } }由上可知: * cancel(int jobId) 会调用 cancelJob(int uid, int jobId),cancelAll() 会调用 cancelJobsForUid(int uid, true)
JobSchedulerService.java
private void cancelJobImpl(JobStatus cancelled) { stopTrackingJob(cancelled); synchronized (mJobs) { // Remove from pending queue. mPendingJobs.remove(cancelled); // Cancel if running. stopJobOnServiceContextLocked(cancelled); } }作用是找到匹配的正在执行 job,则取消该 job
JobServiceContext.java
void cancelExecutingJob() { mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget(); }向运行在 system_server 主线程的 JobServiceHandler 发送 MSG_CANCEL 消息,接收到该消息,则执行handleCancelH()
JobServiceContext.java :: JobServiceHandler
private void handleCancelH() { switch (mVerb) { case VERB_BINDING: case VERB_STARTING: mCancelled.set(true); break; case VERB_EXECUTING: if (hasMessages(MSG_CALLBACK)) { // If the client has called jobFinished, ignore this cancel. return; } sendStopMessageH(); break; case VERB_STOPPING: // Nada. break; default: break; } }JobServiceContext.java :: JobServiceHandler
private void sendStopMessageH() { removeOpTimeOut(); if (mVerb != VERB_EXECUTING) { closeAndCleanupJobH(false /* reschedule */); return; } try { mVerb = VERB_STOPPING; scheduleOpTimeOut(); service.stopJob(mParams); } catch (RemoteException e) { closeAndCleanupJobH(false /* reschedule */); } }之后的过程与 schedule 的过程大同小异
由上面的知识点可知无论是 JobScheduler 的 schedule 过程还是 cancel 过程,都涉及到两次的跨进程调用:
1.从 app 进程进入 system_server 进程的 JobSchedulerStub,采用 IJobScheduler 接口
2.system_server 进程的主线程来执行 bind Service,从而拉起目标进程,服务启动后会回调到发起端的 onServiceConnected;this.service 是指获取远程 IJobService 的代理端,然后通过 IJobService 接口,再次调用到 app 端
3.最终会回调目标应用中的 JobService 的 onStartJob() 方法,可见该方法运行在 app 进程的主线程,那么当存在耗时操作时则必须要采用异步方式,让耗时操作交给子线程去执行,这样就不会阻塞 app 的 UI 线程
本文参考博文