Android O - java.lang.RuntimeException: An error occurred while executing doInBackground()

最近任務列表中多了一個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();
                        }
                    }
                }
            }
        }
    }
    
  • 上面的代碼是在源碼的基礎上僅僅在12處做了Exception的處理,其餘的地方沒有變化,可以對比源碼查看比較。

  • 如果你的項目中有的三方庫中已經引入了這個SafeJobIntentService類,但是因爲你使用不了它們的這個類,而你再引用比如implementation 'com.evernote:android-job:1.4.2'庫的話,就會出現duplicate class found in the module, 如果出現這種問題,我們可以通過重新命名類的方式,按照上面的代碼來做處理就好了。

  • 希望Google在未來的庫中加入這個問題的解決辦法。

JSON三種數據解析方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章