最近任务列表中多了一个bug,让在这个sprint中解决掉,bug的堆栈信息如下:
Fatal Exception: java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:353)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by java.lang.SecurityException: Caller no longer running, last stopped +25s437ms because: timed out while starting
at android.os.Parcel.readException(Parcel.java:1942)
at android.os.Parcel.readException(Parcel.java:1888)
at android.app.job.IJobCallback$Stub$Proxy.dequeueWork(IJobCallback.java:191)
at android.app.job.JobParameters.dequeueWork(JobParameters.java:196)
at android.support.v4.app.JobIntentService$JobServiceEngineImpl.dequeueWork(JobIntentService.java:309)
at android.support.v4.app.JobIntentService.dequeueWork(JobIntentService.java:627)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:384)
at android.support.v4.app.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:377)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
-
由上面的bug信息可知是调用了系统
JobIntentService
中的AsyncTask
中的doInBackground
所致, 而doInBackground
又调用了dequeueWork
,下面是源码部分(androidx 1.1.0的源码):final class CommandProcessor extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { GenericWorkItem work; if (DEBUG) Log.d(TAG, "Starting to dequeue work..."); while ((work = dequeueWork()) != null) { if (DEBUG) Log.d(TAG, "Processing next work: " + work); onHandleWork(work.getIntent()); if (DEBUG) Log.d(TAG, "Completing work: " + work); work.complete(); } if (DEBUG) Log.d(TAG, "Done processing work!"); return null; }
-
dequeueWork()
的源码如下, 我们关注mJobImpl != null
的部分, 又会进入mJobImpl.dequeueWork()
的部分:GenericWorkItem dequeueWork() { if (mJobImpl != null) { return mJobImpl.dequeueWork(); } else { synchronized (mCompatQueue) { if (mCompatQueue.size() > 0) { return mCompatQueue.remove(0); } else { return null; } } } }
-
mJobImpl
其实是一个CompatJobEngine
类型,源码以及是实现类JobServiceEngineImpl
如下:interface CompatJobEngine { IBinder compatGetBinder(); GenericWorkItem dequeueWork(); } @RequiresApi(26) static final class JobServiceEngineImpl extends JobServiceEngine implements JobIntentService.CompatJobEngine { @Override public JobIntentService.GenericWorkItem dequeueWork() { JobWorkItem work; synchronized (mLock) { if (mParams == null) { return null; } work = mParams.dequeueWork(); } if (work != null) { work.getIntent().setExtrasClassLoader(mService.getClassLoader()); return new WrapperWorkItem(work); } else { return null; } } }
-
从文章开头的bug信息中看到,接着又进入到了
mParams.dequeueWork();
中,然后就进入到了Binder
机制中,源码如下,由此可以断定是在这里出了问题,抛出了异常,但是因为这里属于源码部分,不应该属于我们的职责范围。public @Nullable JobWorkItem dequeueWork() { try { return getCallback().dequeueWork(getJobId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @hide */ @UnsupportedAppUsage public IJobCallback getCallback() { return IJobCallback.Stub.asInterface(callback); }
-
经过查询源码,发现问题出现在framework层,而且网上早已有这个问题的issue:
https://github.com/evernote/android-job/issues/255
https://issuetracker.google.com/issues/63622293 -
网上遇到这类问题的人很多很多,但是到目前为止,我查看了最新的google的androidx库(
"androidx.core:core-ktx:1.2.0-rc01"
),依旧是没有解决这个问题。 -
其实查看源码后发现解决这个问题的方法也比较简单,就是我们可以新建一个
androidx.core.app
的包,在其中新建一个类SafeJobIntentService
继承JobIntentService
, 之所以这么做是因为dequeueWork()
方法的权限不是public,我们得写在同一个包中才能重写它的方法,进行bug的修复。@RestrictTo({Scope.LIBRARY}) public abstract class SafeJobIntentService extends JobIntentService { public SafeJobIntentService() { } GenericWorkItem dequeueWork() { try { return super.dequeueWork();//1 这里我们对此方法进行了try/catch操作 } catch (SecurityException var2) { var2.printStackTrace(); return null; } } public void onCreate() { super.onCreate(); if (VERSION.SDK_INT >= 26) { this.mJobImpl = new SafeJobServiceEngineImpl(this); } else { this.mJobImpl = null; } } } @RequiresApi(26) public class SafeJobServiceEngineImpl extends JobServiceEngine implements CompatJobEngine { static final String TAG = "JobServiceEngineImpl"; static final boolean DEBUG = false; final JobIntentService mService; final Object mLock = new Object(); JobParameters mParams; SafeJobServiceEngineImpl(JobIntentService service) { super(service); this.mService = service; } public IBinder compatGetBinder() { return this.getBinder(); } public boolean onStartJob(JobParameters params) { this.mParams = params; this.mService.ensureProcessorRunningLocked(false); return true; } public boolean onStopJob(JobParameters params) { boolean result = this.mService.doStopCurrentWork(); synchronized(this.mLock) { this.mParams = null; return result; } } public GenericWorkItem dequeueWork() { JobWorkItem work = null; synchronized(this.mLock) { if (this.mParams == null) { return null; } try { work = this.mParams.dequeueWork(); } catch (SecurityException var5) { var5.printStackTrace(); } } if (work != null) { work.getIntent().setExtrasClassLoader(this.mService.getClassLoader()); return new SafeJobServiceEngineImpl.WrapperWorkItem(work); } else { return null; } } final class WrapperWorkItem implements GenericWorkItem { final JobWorkItem mJobWork; WrapperWorkItem(JobWorkItem jobWork) { this.mJobWork = jobWork; } public Intent getIntent() { return this.mJobWork.getIntent(); } public void complete() { synchronized(SafeJobServiceEngineImpl.this.mLock) { if (SafeJobServiceEngineImpl.this.mParams != null) { try { SafeJobServiceEngineImpl.this.mParams.completeWork(this.mJobWork); } catch (SecurityException | IllegalArgumentException var4) { // 2 这里我们也对completeWork进行了try/catch操作 var4.printStackTrace(); } } } } } }
-
上面的代码是在源码的基础上仅仅在
1
和2
处做了Exception
的处理,其余的地方没有变化,可以对比源码查看比较。 -
如果你的项目中有的三方库中已经引入了这个
SafeJobIntentService
类,但是因为你使用不了它们的这个类,而你再引用比如implementation 'com.evernote:android-job:1.4.2'
库的话,就会出现duplicate class found in the module
, 如果出现这种问题,我们可以通过重新命名类的方式,按照上面的代码来做处理就好了。 -
希望Google在未来的库中加入这个问题的解决办法。