最近任務列表中多了一個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在未來的庫中加入這個問題的解決辦法。