在上篇 《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内。