JobService源码探究之 onStartJob()里执行耗时逻辑导致Job可能被强制销毁

简介: JobService源码探究之 onStartJob()里执行耗时逻辑导致Job可能被强制销毁

在上篇 《JobService源码探究之 onStartJob()里如何优雅地处理耗时逻辑?》里我们留下了如下两个疑问。

在本篇里一一解答。

疑问一

为什么onStartJob()直接执行耗时逻辑后,即便自己没有finish该Job,但是Job还是会被自动销毁?

疑问二

为什么onStartJob()里开启新线程执行的耗时逻辑超过10min,但是Job被自动停止和销毁?



这两个疑问的有如下区别:

耗时逻辑所属的线程

疑问一 UI线程

疑问二 后台线程



Job最后的结果

疑问一 被自动销毁

疑问二 被自动停止和销毁

但他们之间也存在共同点,就是都是某种程度上的超时。

疑问一 onStartJob()耗时,导致本函数没有及时执行结束,属于onStartJob()回调超时。

疑问二 onStartJob()并没有耗时,但是Job的后台任务执行时间过长,属于Job执行超时。

我们看看这两种情况下系统输出的Log。

疑问一

JobServiceContext: No response from client for onStartJob ef7724f #u0a174/0 com.example.timeapidemo/.EllisonsJobService

疑问二

JobServiceContext( 1433): Client timed out while executing (no jobFinished received), sending onStop: 5673577 #u0a17

按照上述log,我们检索源码得知,这两句话出自如下函数。



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

    private void handleOpTimeoutLocked() {
        switch (mVerb) {
            ...
            case VERB_STARTING:
                // Client unresponsive - wedged or failed to respond in time. We don't really
                // know what happened so let's log it and notify the JobScheduler
                // FINISHED/NO-RETRY.
                Slog.w(TAG, "No response from client for onStartJob " +
                        mRunningJob != null ? mRunningJob.toShortString() : "<null>");
                closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");★1
                break;
            ...
            case VERB_EXECUTING:
                // Not an error - client ran out of time.
                Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
                        "sending onStop: "  +
                        mRunningJob != null ? mRunningJob.toShortString() : "<null>");
                mParams.setStopReason(JobParameters.REASON_TIMEOUT);
                sendStopMessageLocked("timeout while executing");★2
                break;
            ...
        }
    }

★1可以看到当Job处在STARTING状态的时候发生超时的话,系统将直接去执行JobService的结束处理,这里面会去解绑JobService并销毁。



★2可以看到当Job处在EXECUTING状态的时候发生超时的话,系统将直接去执行JobService的停止处理,之后再去执行JobService的结束处理。



检索handleOpTimeoutLocked()的调用,发现是内部类JobServiceHandler调用的。

    private class JobServiceHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_TIMEOUT:
                    synchronized (mLock) {
                        if (message.obj == mRunningCallback) {
                            handleOpTimeoutLocked();
                        }...
                    }
                ...
            }
        }
    }

检索MSG_TIMEOUT msg发出的地方,如下:

    private void scheduleOpTimeOutLocked() {
        ...
        final long timeoutMillis;
        switch (mVerb) {
            case VERB_EXECUTING:
                timeoutMillis = EXECUTING_TIMESLICE_MILLIS; // 10mins
                break;
            case VERB_BINDING:
                timeoutMillis = OP_BIND_TIMEOUT_MILLIS; // 18s
                break;
            default:
                timeoutMillis = OP_TIMEOUT_MILLIS; // 8s
                break;
        }
        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
        mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
        mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
    }

检索scheduleOpTimeOutLocked()调用的地方,我们发现如下场合的时候都会调用该方法发送一定时间的延时MSG。



●executeRunnableJob() JobService刚开始绑定的时候,发送18s的延时MSG



●handleServiceBoundLocked() 回调JobService的onStartJob()的时候,发送8s的延时MSG



●handleStartedLocked() JobService的onStartJob()回调成功后,发送10mins的延时MSG



●sendStopMessageLocked() 回调JobService的onStopJob()的时候,发送8s的延时MSG



以上可以看出。

疑问一的原因是

onStartJob()没有及时在8s内完成回调,导致JobScheduler自行销毁了该JobService。

而且这时候JobScheduler在结束该Job的时候设置needsReschedule为false,所以这种情况下被销毁了的Job无法自启动。

疑问二的原因是

onStartJob()回调后没有在10mins内调用JobFinished方法通知JobScheduler,导致JobScheduler强制停止了该JobService。



到这里,上面两个疑问都解答了。



我们不禁得到如下启发:

启发一

如果非得在onStartJob()里执行任务逻辑的话,请不要超过8s。

启发二

如果在onStartJob()里已经开启了线程执行任务逻辑,但是处理时间尽量控制在10mins内。

相关文章
|
29天前
|
Java 调度
Java实现定时启动,且只执行一次,如何实现?
【10月更文挑战第18天】Java实现定时启动,且只执行一次,如何实现?
173 3
|
1月前
|
缓存 小程序 UED
如何利用小程序的生命周期函数实现数据的加载和更新?
如何利用小程序的生命周期函数实现数据的加载和更新?
65 4
|
2月前
|
缓存 C语言
线程的创建过程
【9月更文挑战第15天】线程是由内核和用户态协同实现的机制。`pthread_create` 函数在 Glibc 中定义,首先处理线程属性参数,如栈大小,默认值或传入值。每个线程有一个 `pthread` 结构来维护状态。创建线程时,需要分配线程栈,并进行以下操作:获取栈大小、设置保护区域、缓存管理、内存映射、栈初始化及保护、填充 `pthread` 结构并管理栈缓存。最终通过 `create_thread` 函数调用 `clone` 系统调用创建线程,共享进程数据结构
|
6月前
|
监控 安全 Java
JVM工作原理与实战(七):类的生命周期-初始化阶段
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了类的生命周期、类的初始化阶段等内容。
52 5
|
Go 数据库
sync.Once-保证运行期间的某段代码只会执行一次
sync.Once-保证运行期间的某段代码只会执行一次
83 0
|
Java
线程池的核心参数及执行原理你知道嘛?
线程池是一种管理和复用线程的机制,它可以提高线程的利用率和系统的性能。
400 0
|
6月前
|
Java Spring 容器
面试题:怎样为组件在创建的时候指定执行一个函数,在销毁的时候也先执行一个函数
面试题:怎样为组件在创建的时候指定执行一个函数,在销毁的时候也先执行一个函数
55 0
|
前端开发 vr&ar
【前端验证】fork-join_none线程立即执行的一次代码优化记录
【前端验证】fork-join_none线程立即执行的一次代码优化记录
|
SQL 存储 Java
java通过拦截器实现项目每次执行sql耗时统计,可配置是否打印
java通过拦截器实现项目每次执行sql耗时统计,可配置是否打印
java线程的三种创建方式详细分析(全)
目录前言1. 继承Thread类2. 实现Runnable 接口3. 通过Callable和Future创建线程4. 总结 前言 关于线程这部分内容讲的比较多,可看我之前的文章 【操作系统】线程与进程的深入剖析(全) 【操作系统】守护线程和守护进程的区别 对于线程Thread的分析 也可看我之前的文章 java之Thread类详细分析(全) java之Thread类实战模板(全) 多线程中run()和start()的异同详细分析(全) 对于创建方式的汇总以及方式 可看我这篇文章的总结对比 1. 继承
102 0
java线程的三种创建方式详细分析(全)
下一篇
无影云桌面