基于 Android 7.1.1 源码分析
前提
接下来,我们来看看 JobServiceService 服务中和 cancel 相关的服务:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56// 取消指定设备用户的所有的 job!
void cancelJobsForUser(int userHandle) {
    List<JobStatus> jobsForUser;
    synchronized (mLock) {
        jobsForUser = mJobs.getJobsByUser(userHandle);
    }
    for (int i=0; i<jobsForUser.size(); i++) {
        JobStatus toRemove = jobsForUser.get(i);
        cancelJobImpl(toRemove, null);
    }
}
// 取消指定 package 和指定 uid 的所有的 job!
void cancelJobsForPackageAndUid(String pkgName, int uid) {
    List<JobStatus> jobsForUid;
    synchronized (mLock) {
        jobsForUid = mJobs.getJobsByUid(uid);
    }
    for (int i = jobsForUid.size() - 1; i >= 0; i--) {
        final JobStatus job = jobsForUid.get(i);
        if (job.getSourcePackageName().equals(pkgName)) {
            cancelJobImpl(job, null);
        }
    }
}
// 取消指定 uid 的应用程序的所有的 job!
public void cancelJobsForUid(int uid, boolean forceAll) {
    List<JobStatus> jobsForUid;
    synchronized (mLock) {
        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, null);
    }
}
// 取消指定 uid 的应用程序的 id 为 jobId 的 job!
public void cancelJob(int uid, int jobId) {
    JobStatus toCancel;
    synchronized (mLock) {
        toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
    }
    if (toCancel != null) {
        cancelJobImpl(toCancel, null);
    }
}
JobSchedulerService 提供了很多个接口来取消 job,但是他们都疯了似的调用了同一个方法,我们来继续看!
1 JSS.cancelJobImpl
同一个取消接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
    if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
    // 停止监视这个 job,writeBack 为 true!
    stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
    synchronized (mLock) {
        // Remove from pending queue.
        // 从 mPendingJobs 中移除!
        if (mPendingJobs.remove(cancelled)) {
            mJobPackageTracker.noteNonpending(cancelled);
        }
        // Cancel if running.
        // 如果正在运行,就要停止这个 job
        stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
        reportActive();
    }
}
这里我们一个一个来看:
1.1 JSS.stopTrackingJob
停止 stopTrackingJob 如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
        boolean writeBack) {
    synchronized (mLock) {
        // Remove from store as well as controllers.
        // 先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件!
        final boolean removed = mJobs.remove(jobStatus, writeBack);
        if (removed && mReadyToRock) {
            for (int i=0; i<mControllers.size(); i++) {
                // 通知控制器,取消 track!
                StateController controller = mControllers.get(i);
                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
            }
        }
        return removed;
    }
}
先从 JobStore 中移除这个 job,因为 writeBack 为 true,则需要更新 jobxs.xml 文件,通知控制器,取消 track!
1.2 JSS.stopJobOnServiceContextLocked
如果这个 job 是在运行中,就要取消它!1
2
3
4
5
6
7
8
9
10
11private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
    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())) {
            jsc.cancelExecutingJob(reason);
            return true;
        }
    }
    return false;
}
每一个运行着的 job,都是和一个 JobServiceContext 绑定着的,这里是遍历所有的 JobServiceContext,找到要 cancel 的 job,调用 jobServiceContext 的 cancelExecutingJob 方法:
1.2.1 JSC.cancelExecutingJob
这个方法很简单,发送了 MSG_CANCEL 消息给 JobServiceHandler,reason 为 JobParameters.REASON_CANCELED1
2
3void cancelExecutingJob(int reason) {
    mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
}
我们继续看,进入 JobServiceHandler:
1.2.2 JSH.MSG_CANCEL
JobServiceHander 会处理 MSG_CANCEL 消息!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38private class JobServiceHandler extends Handler {
    JobServiceHandler(Looper looper) {
        super(looper);
    }
    
    public void handleMessage(Message message) {
            
            ... ... ... ...
            case MSG_CANCEL: // 接受到了 MSG_CANCEL.
                if (mVerb == VERB_FINISHED) { 
                    if (DEBUG) {
                        Slog.d(TAG,
                               "Trying to process cancel for torn-down context, ignoring.");
                    }
                    return;
                }
                mParams.setStopReason(message.arg1); // reason 为 JobParameters.REASON_CANCELED
                if (message.arg1 == JobParameters.REASON_PREEMPT) { // 不进入,因为这不是优先级取代!
                    mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
                            NO_PREFERRED_UID;
                }
                handleCancelH();
                break;
            case MSG_TIMEOUT:
                handleOpTimeoutH();
                break;
            case MSG_SHUTDOWN_EXECUTION:
                closeAndCleanupJobH(true /* needsReschedule */);
                break;
            default:
                Slog.e(TAG, "Unrecognised message: " + message);
        }
    }
    ... ... ... ... ...
}
调用 handleCancelH 方法来取消 Job:
1.2.3 JSH.handleCancelH
1  | /**  | 
我们来看看 sendStopMessageH 方法:
1.2.4 JSH.sendStopMessageH
1  | /**  | 
这里会根据 mVerb 中存储的 job 的动作状态,来做相应的处理:
如果 job 的状态已经不是 VERB_EXECUTING,那就清除资源,恢复初始化;
否则,job 状态设置为 VERB_STOPPING,调用 jobService 的 stopJob,停止服务!
1.2.4.1 JobService.stopJob
通过 Binder 机制,停止 job:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public abstract class JobService extends Service {
    
    ... ... ... ...    
    private static final String TAG = "JobService";
    static final class JobInterface extends IJobService.Stub {
        final WeakReference<JobService> mService;
        JobInterface(JobService service) {
            mService = new WeakReference<>(service);
        }
        
        public void startJob(JobParameters jobParams) throws RemoteException {
            JobService service = mService.get();
            if (service != null) {
                service.ensureHandler();
                Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
                m.sendToTarget();
            }
        }
        
        public void stopJob(JobParameters jobParams) throws RemoteException {
            JobService service = mService.get();
            if (service != null) {
                service.ensureHandler(); // 确保 Handler 已经创建
                Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams); // 发送 MSG_STOP_JOB 到 JobHandler!
                m.sendToTarget();
            }
        }
    }
    ... ... ... ...
}
进入 JobHandler 方法:
1.2.4.2 JobService.JobHandler
处理前面通过 Binder 发送过来的 MSG_STOP_JOB:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45class JobHandler extends Handler {
    JobHandler(Looper looper) {
        super(looper);
    }
    
    public void handleMessage(Message msg) {
        final JobParameters params = (JobParameters) msg.obj;
        switch (msg.what) {
            ... ... ... ...
            case MSG_STOP_JOB:
                try {
                    // 这里是获得 JobService 的 onStopJob 的返回值!
                    boolean ret = JobService.this.onStopJob(params);
                    ackStopMessage(params, ret);
                } catch (Exception e) {
                    Log.e(TAG, "Application unable to handle onStopJob.", e);
                    throw new RuntimeException(e);
                }
                break;
           ... ... ... ...
            default:
                Log.e(TAG, "Unrecognised message received.");
                break;
        }
    }
    ... ... ... ...    
 
    private void ackStopMessage(JobParameters params, boolean reschedule) {
        final IJobCallback callback = params.getCallback();
        final int jobId = params.getJobId();
        if (callback != null) {
            try {
                // 将被停止的 job 的 id 和 onStopJob 的返回值发给 JobServiceContext!
                callback.acknowledgeStopMessage(jobId, reschedule);
            } catch(RemoteException e) {
                Log.e(TAG, "System unreachable for stopping job.");
            }
        } else {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Attempting to ack a job that has already been processed.");
            }
        }
    }
}
JobService 的 onStopJob 的返回值如果为 true,那么,就表示 JobService 需要重新拉起他!前面我们分析的时候,知道,callback 就是 JobServiceContext
1.2.4.3 JSC.acknowledgeStopMessage
接着发送 MSG_CALLBACK 给 JobServiceHandler:1
2
3
4
5
6
7
8
public void acknowledgeStopMessage(int jobId, boolean reschedule) {
    if (!verifyCallingUid()) {
        return;
    }
    mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
            .sendToTarget();
}
我们进入 JobServiceHandler 方法里面看看:
1.2.4.4 JSC.JobServiceHandler
1  | private class JobServiceHandler extends Handler {  | 
可以看到,最后会调用 JSH 的 closeAndCleanupJobH 方法:
1.2.4.5 JSH.closeAndCleanupJobH
如果 job 的状态已经不是 VERB_EXECUTING,那就清除资源,恢复初始化;这个方法很简单,就是将 JobServiceContext 中的属性值恢复初始化,表示没有任何 job 在运行!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34private void closeAndCleanupJobH(boolean reschedule) {
    final JobStatus completedJob;
    synchronized (mLock) {
        if (mVerb == VERB_FINISHED) {
            return;
        }
        completedJob = mRunningJob;
        mJobPackageTracker.noteInactive(completedJob);
        try {
            mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
                    mRunningJob.getSourceUid());
        } catch (RemoteException e) {
            // Whatever.
        }
        if (mWakeLock != null) {
            mWakeLock.release();
        }
        mContext.unbindService(JobServiceContext.this); // 取消绑定 jobSerivce
        mWakeLock = null;
        mRunningJob = null; // mRunningJob 的值置为 null!
        mParams = null; // JobParamters 置为 null
        mVerb = VERB_FINISHED; // mVerb 的状态值为 VERB_FINISHED
        mCancelled.set(false); // mCancelled 置为 false
        service = null; // 应用的 JobService 代理对象置为 null
        mAvailable = true; // mAvailable 置为 true,表示这个 JobService 可以分配给其他 job
    }
    removeOpTimeOut();
    removeMessages(MSG_CALLBACK); // 清除处理过的 MSG
    removeMessages(MSG_SERVICE_BOUND);
    removeMessages(MSG_CANCEL);
    removeMessages(MSG_SHUTDOWN_EXECUTION);
    // 回调 JobSchedulerService 的 onJobCompleted 方法!
    mCompletedListener.onJobCompleted(completedJob, reschedule);
}
恢复初始化,为下次做准备!
1.2.4.5.1 JSC.onServiceDisconnected
取消绑定 JobService 会调用 onServiceDisconnected1
2
3
4
5/** If the client service crashes we reschedule this job and clean up. */
public void onServiceDisconnected(ComponentName name) {
    mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
}
这里的会再回到 JobServiceHandler 中:1
2
3case MSG_SHUTDOWN_EXECUTION:
    closeAndCleanupJobH(true /* needsReschedule */);
    break;
做第二次 clean,防止第一次不成功,其实第一次 clean 成功的话,mVerb 的值为 VERB_FINISHED,第二次 clean 会自动退出的!
1.2.4.5.2 JSS.onJobCompleted
我们进入 JSS 的 onJobCompleted 的方法中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
    if (DEBUG) {
        Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
    }
    // Do not write back immediately if this is a periodic job. The job may get lost if system
    // shuts down before it is added back.
    // 再次停止 track 这个 job,这里 stopTrackingJob 的返回值为 false!
    if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
        if (DEBUG) {
            Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
        }
        // We still want to check for jobs to execute, because this job may have
        // scheduled a new job under the same job id, and now we can run it.
        // 发送 MSG_CHECK_JOB_GREEDY,继续执行其他的 job,然后直接 return
        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
        return;
    }
    // Note: there is a small window of time in here where, when rescheduling a job,
    // we will stop monitoring its content providers.  This should be fixed by stopping
    // the old job after scheduling the new one, but since we have no lock held here
    // that may cause ordering problems if the app removes jobStatus while in here.
    if (needsReschedule) {
        JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
        startTrackingJob(rescheduled, jobStatus);
    } else if (jobStatus.getJob().isPeriodic()) {
        JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
        startTrackingJob(rescheduledPeriodic, jobStatus);
    }
    reportActive();
    mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
这里首先调用了 stopTrackingJob 将这个 job 从 JobStore 和 controller 中移除,因为之前已经移除过了,所以这个 stopTrackingJob 的返回值为 false1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
 * object removed.
 */
private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
        boolean writeBack) {
    synchronized (mLock) {
        // Remove from store as well as controllers.
        // 尝试从 JobStore 中移除这个 job,注意,在之前已经移除过了,所以,这里的 removed 为 false!
        final boolean removed = mJobs.remove(jobStatus, writeBack);
        if (removed && mReadyToRock) {
            for (int i=0; i<mControllers.size(); i++) {
                StateController controller = mControllers.get(i);
                // 从 Controller 的跟踪队列中移除!
                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
            }
        }
        return removed;
    }
}
可以看出,对于用户主动 cancel 的任务,无论 onStopJob 的返回值是什么,都不会 reschedule 了!