前言
上篇文章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 流程
先给出 JobScheduler 的 schedule 流程时序图如下:
1.1 JobSchedulerService.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;
}
1.1.1 创建 JobStatus
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;
}
}
1.2 JobSchedulerService.cancelJob
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);
}
}
1.3 JobSchedulerService.startTrackingJob
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
1.4 JobSchedulerService.JobHandler
1.4.1 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);
}
}
1.4.2 maybeQueueReadyJobsForExecutionLockedH
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 中
1.4.3 isReadyToBeExecutedLocked
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
1.4.4 maybeRunPendingJobsH
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 已经在运行中了则跳过)
1.5 executeRunnableJob
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
1.5.1 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 的主线程
1.5.2 Send MSG_SERVICE_BOUND
JobServiceContext.java :: JobServiceHandler
private class JobServiceHandler extends Handler {
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SERVICE_BOUND:
removeOpTimeOut();
handleServiceBoundH();
break;
...
}
}
}
1.5.3 handleServiceBoundH
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进程
1.6 JobService.startJob
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进程的主线程。
1.7 JobService.JobHandler
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;
}
}
}
二、JobScheduler 的 cancel 流程
2.1 JobSchedulerStub.cancel
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);
}
}
}
2.2 JobSchedulerService.cancelJob
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)
2.3 JobSchedulerService.cancelJobImpl
JobSchedulerService.java
private void cancelJobImpl(JobStatus cancelled) {
stopTrackingJob(cancelled);
synchronized (mJobs) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
// Cancel if running.
stopJobOnServiceContextLocked(cancelled);
}
}
2.4 JobSchedulerService.stopJobOnServiceContextLocked
private boolean stopJobOnServiceContextLocked(JobStatus job) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJob();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
// 找到匹配的正在执行 job,则取消该 job
jsc.cancelExecutingJob();
return true;
}
}
return false;
}
作用是找到匹配的正在执行 job,则取消该 job
2.5 JobServiceContext.cancelExecutingJob
JobServiceContext.java
void cancelExecutingJob() {
mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
}
向运行在 system_server 主线程的 JobServiceHandler 发送 MSG_CANCEL 消息,接收到该消息,则执行handleCancelH()
2.6 JobServiceContext.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;
}
}
2.7 JobServiceContext.sendStopMessageH
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 线程
本文参考博文