最近任务列表中多了一个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 {
@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在未来的库中加入这个问题的解决办法。
JSON三种数据解析方法