JobService源码探究之 Job自行Cancel后即使onStopJob返回true也不会被再启动

简介: JobService源码探究之 Job自行Cancel后即使onStopJob返回true也不会被再启动

上章节遗留的疑问二,本章将继续探究缘由。

疑问二

自行cancel了JobService后,onStopJob()即使返回true也不能被重新启动?



照例直接上源码。

cancel是IJobScheduler的API,我们直接查看该API的实现。



frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java

public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
    ...
    final class JobSchedulerStub extends IJobScheduler.Stub {
        ...
        @Override
        public void cancel(int jobId) throws RemoteException {
            final int uid = Binder.getCallingUid();
            long ident = Binder.clearCallingIdentity();
            try {
                JobSchedulerService.this.cancelJob(uid, jobId);★1
            }...
        }
    }
    ...
    public void cancelJob(int uid, int jobId) {
        JobStatus toCancel;
        synchronized (mLock) {
            toCancel = mJobs.getJobByUidAndJobId(uid, jobId); // 从JobStore中获取指定UID下的指定ID的JobStatus
            if (toCancel != null) {
                cancelJobImplLocked(toCancel, null, "cancel() called by app");★2
            }
        }
    }
    ...
    private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
        cancelled.unprepareLocked(ActivityManager.getService());
        stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); // 从JobStore中移除该Job的追踪记录
        // Remove from pending queue.
        if (mPendingJobs.remove(cancelled)) {// 从运行中JobStatus列表中移除该Job
            mJobPackageTracker.noteNonpending(cancelled);
        }
        // Cancel if running.
        stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);★3 即将执行Job停止处理
        reportActiveLocked();
    }
    ...
    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
        for (int i=0; i<mActiveServices.size(); i++) {
            JobServiceContext jsc = mActiveServices.get(i);
            final JobStatus executing = jsc.getRunningJobLocked();
            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
                jsc.cancelExecutingJobLocked(reason, debugReason);★4告诉JobServiceContext去停止该Job
                return true;
            }
        }
        return false;
    }
}

我们继续看看JobServiceContext具体做了什么处理。

public final class JobServiceContext implements ServiceConnection {
    ...
    void cancelExecutingJobLocked(int reason, String debugReason) {
        doCancelLocked(reason, debugReason);★1
    }
    ...
    void doCancelLocked(int arg1, String debugReason) {
        ...
        mParams.setStopReason(arg1);// 设置REASON_CANCELED作为停止Job原因
        if (arg1 == JobParameters.REASON_PREEMPT) {
            mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
                    NO_PREFERRED_UID;
        }
        handleCancelLocked(debugReason);★2
    }
    ...
    private void handleCancelLocked(String reason) {
        ...
        switch (mVerb) {
            case VERB_BINDING:
            case VERB_STARTING:
                mCancelled = true;
                applyStoppedReasonLocked(reason);★3-1 如果JobService还未执行走这个处理
                break;
            case VERB_EXECUTING:
                sendStopMessageLocked(reason);★3-2 如果JobService已经执行走这个处理
                break;
               ...
        }
    }
    // 假设JobService已经执行,那直接走到这
    private void sendStopMessageLocked(String reason) {
        removeOpTimeOutLocked();
        ...
        try {
            mVerb = VERB_STOPPING;//将Job状态置为VERB_STOPPING
            scheduleOpTimeOutLocked();
            service.stopJob(mParams);★4 IPC调用JobService的onStopJob()
        }...
    }
}

同样的JobServcie处理完onStopJob()后会将返回值通过IJobCallback会发给JobServiceContext。

public abstract class JobServiceEngine {
    static final class JobInterface extends IJobService.Stub {
        ...
        public void stopJob(JobParameters jobParams) throws RemoteException {
            JobServiceEngine service = mService.get();
            if (service != null) {
                Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);★1
                m.sendToTarget();
            }
        }
    }
    ...
    class JobHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message msg) {
            final JobParameters params = (JobParameters) msg.obj;
            switch (msg.what) {
                case MSG_STOP_JOB:
                    try {
                        boolean ret = JobServiceEngine.this.onStopJob(params);★2
                        ackStopMessage(params, ret);★3
                    }...
                ...
            }
        }
    }
    ...
    private void ackStopMessage(JobParameters params, boolean reschedule) {
        final IJobCallback callback = params.getCallback();
        final int jobId = params.getJobId();
        if (callback != null) {
            try {
                callback.acknowledgeStopMessage(jobId, reschedule);★4
            }...
        }...
   }
}

我们假设onStopJob()返回的是true,那么★4传给JobServiceContext的reschedule就是true。



再次回到JobServiceContext。

public final class JobServiceContext implements ServiceConnection {
    ...
    final class JobCallback extends IJobCallback.Stub {
        @Override
        public void acknowledgeStopMessage(int jobId, boolean reschedule) {
            doAcknowledgeStopMessage(this, jobId, reschedule);★1
        }
        ...
    }
    ...
    void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
        doCallback(cb, reschedule, null);★2
    }
    ...
    void doCallback(JobCallback cb, boolean reschedule, String reason) {
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                if (!verifyCallerLocked(cb)) {
                    return;
                }
                doCallbackLocked(reschedule, reason);★3 此刻的reschedule为true
            }
        }...
    }
    ...
    void doCallbackLocked(boolean reschedule, String reason) {
        ...
        removeOpTimeOutLocked();//在这里移除回调onStopJob时发送的8s延时msg
        if (mVerb == VERB_STARTING) {
            handleStartedLocked(reschedule);
        } else if (mVerb == VERB_EXECUTING ||
                mVerb == VERB_STOPPING) {
            handleFinishedLocked(reschedule, reason);★4 此刻状态为STOPPING,调用finish逻辑
        }...
    }
    ...
    private void handleFinishedLocked(boolean reschedule, String reason) {
        switch (mVerb) {
            case VERB_EXECUTING:
            case VERB_STOPPING:
                closeAndCleanupJobLocked(reschedule, reason);★5 
                break;
            ...
        }
    }
    ...
    private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
        ...
        applyStoppedReasonLocked(reason);
        completedJob = mRunningJob;
        mJobPackageTracker.noteInactive(completedJob);
        ...
        if (mWakeLock != null) {
            mWakeLock.release();//释放WakeLock
        }
        mContext.unbindService(JobServiceContext.this);★6告诉AMS解绑该JobService,最终会调到它的onDestroy()
        mWakeLock = null;
        mRunningJob = null;
        mRunningCallback = null;
        mParams = null;
        mVerb = VERB_FINISHED;// 将Job状态置为VERB_FINISHED
        mCancelled = false;
        service = null;
        mAvailable = true;
        removeOpTimeOutLocked();
        mCompletedListener.onJobCompletedLocked(completedJob, reschedule); ★7 将结束的Job当作参数调用结束后回调处理
    }
    ...
    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
        ...
        final JobStatus rescheduledJob = needsReschedule
                ? getRescheduleJobForFailureLocked(jobStatus) : null; // 从结束的Job中复制出新的JobStatus
        ...
        // 从JobStore中移除原有Job的追踪记录,因为之前的JobStatus在cancel的时候已经从JobStore中移除了。所以本函数返回false
        // 然后发送MSG_CHECK_JOB_GREEDY的msg
        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
            ...
            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();★8
            return;
        }
        ...
    }
    final private class JobHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                ...
                switch (message.what) {
                    ...
                    case MSG_CHECK_JOB_GREEDY:
                        queueReadyJobsForExecutionLocked();★9 将准备好执行的Jobs排入队列
                        break;
                    ...
                }
                maybeRunPendingJobsLocked(); ★10 轮循mPendingJobs准备执行JobService
                ...
            }
        }
    }
    private void queueReadyJobsForExecutionLocked() {
        ...
        // ★9-1 遍历通过JobStore中存储的所有Job,然后回调ReadyQueueFunctor添加Job到ReadyJobQueueFunctor中
        mJobs.forEachJob(mReadyQueueFunctor);
        // 将ReadyJobQueueFunctor中保存的jobs列表添加到待执行列表mPendingJobs中
        mReadyQueueFunctor.postProcess();
    }
}

执行cancel处理的时候已经从JobStore里移除了原有的JobStatus对象,

同时在发送MSG_CHECK_JOB_GREEDY前有没有将新的JobStatus对象保存到JobStore中。



所以★9-1在遍历存储的Jobs时候,并不会将新的JobStatus插入到待执行队列里。

进而导致★10的时候不会重新执行该JobService。



那为什么发生条件不满足时候Job被强制停止后能再度启动呢?

查看源码我们知道。



比如设备进入IDLE状态的时候,JobSchedulerService直接通知JobServiceContext去取消没有设置IDLE条件下运行的Job。

而并没有事先去JobStore理移除该Job。



等到JobServiceContext成功停止了该Job后。

JobSchedulerService在执行Job结束的回调(onJobCompletedLocked)时,将会把JobStore里保存的原有JobStatus移除,然后添加为新创建的JobStaus。



这样一来在MSG_CHECK_JOB_GREEDY的msg响应的时候,新的Job就可以得到执行。



到这里,疑问二就解决了。



下次我们探讨下之前遗留的两个思考以及一个总结。

思考一

如果我们在onStartJob()里处理耗时逻辑,导致onStartJob()没有及时返回给JobSchedulerContext。

最终结果是怎么样?



是ANR?

还是因为超时,该Job可能被强制停止和销毁?

思考二

如果onStartJob()里起了新线程处理耗时逻辑,但是返回值返回了false,那么系统还会销毁Job吗?

如果会的话,新线程是否会导致内存泄漏?

总结

JobService和Service的区别


相关文章
|
2月前
|
存储 前端开发 JavaScript
node中循环异步的问题[‘解决方案‘]_源于map循环和for循环对异步事件配合async、await的支持
本文探讨了在Node.js中处理循环异步操作的问题,比较了使用map和for循环结合async/await处理异步事件的差异,并提供了解决方案。
39 0
|
4月前
|
存储 缓存 Serverless
函数计算操作报错合集之如何处理运行时报错:“Function time out after 600 seconds”
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
|
5月前
|
监控 Serverless Shell
函数计算操作报错合集之 显示"Function timed out after 30 seconds (maxMemoryUsage: 73.38MB)" ,该如何解决
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
|
4月前
|
存储
向量化代码实践问题之Task<T>类中的on_completed函数是如何工作的
向量化代码实践问题之Task<T>类中的on_completed函数是如何工作的
|
5月前
|
消息中间件 监控 前端开发
函数计算操作报错合集之当遇到错误信息为Concurrent request count exceeded时,该如何解决
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
110 0
|
小程序
小程序:下拉刷新的简单方法"enablePullDownRefresh": true
小程序:下拉刷新的简单方法"enablePullDownRefresh": true
149 0
|
6月前
|
Java Linux 调度
Java【付诸实践 03】Spring定时任务注解@Scheduled+@EnableAsync用法详解(简单说明+应用场景+demo源代码+执行过程分析)
Java【付诸实践 03】Spring定时任务注解@Scheduled+@EnableAsync用法详解(简单说明+应用场景+demo源代码+执行过程分析)
129 2
|
6月前
|
XML Java 数据格式
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
面试题:怎样把所有的组件的lazy-init值都设置为默认true?
50 0
|
Java API 调度
JobService源码探究之 onStartJob()返回false立马被destroy
JobService源码探究之 onStartJob()返回false立马被destroy
JobService源码探究之 onStartJob()返回false立马被destroy
ts重点学习50-函数得参数得处理方式笔记
ts重点学习50-函数得参数得处理方式笔记
101 0