您的位置:首页 > 移动开发 > Android开发

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

2018-02-06 17:24 525 查看
在上篇《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 #u0a174/0 com.example.timeapidemo/.EllisonsJobService


按照上述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内。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息