Recently, a bug was added to the task list to be resolved in this sprint. The stack information of the bug is as follows:
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)
According to the above bug information, it can be known that the system JobIntentService
, AsyncTask doInBackground is called, while doInBackground
calls dequeueWork
. The following is the source code (source code of 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()
source code is as follows, let’s focus on mJobImpl! = null
part, will enter mjobimpl.dequeuework ()
part:
GenericWorkItem dequeueWork() {
if (mJobImpl != null) {
return mJobImpl.dequeueWork();
} else {
synchronized (mCompatQueue) {
if (mCompatQueue.size() > 0) {
return mCompatQueue.remove(0);
} else {
return null;
}
}
}
}
mJobImpl
is actually a , CompatJobEngine
, source code and is the implementation class JobServiceEngineImpl
as follows:
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;
}
}
}
As you can see from the bug information at the beginning of the article, it goes to mparams.dequeuework (); Binder
, then enter the Binder
mechanism, the source code is as follows, so we can conclude that there is a problem here, throwing an exception, but because this is part of the source code, it should not be our responsibility.
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);
}
After query source, found that the problem appeared in the framework layer, and there are already online issue of the problem: https://github.com/evernote/android-job/issues/255
https://issuetracker.google.com/issues/63622293 online encounter this kind of problem a lot of a lot of people, but so far, I have checked the latest Google androidx library ("androidx.core:core-ktx:1.2.0-rc01"
) and still haven’t solved this problem. App . In this package, a new class SafeJobIntentService</code b> is inserted into the
JobIntentService
. The reason for this is that the dequeueWork()
method is not public. We have to write in the same package to override its methods and fix bugs.
@RestrictTo({Scope.LIBRARY})
public abstract class SafeJobIntentService extends JobIntentService {
public SafeJobIntentService() {
}
GenericWorkItem dequeueWork() {
try {
return super.dequeueWork();//1 Here we do a try/catch operation on this method
} 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 Here we also perform a try/catch operation on the completeWork
var4.printStackTrace();
}
}
}
}
}
}
On the basis of the source code, the above code only handles Exception
at 1
and 2</code b>. The rest of the code does not change, so we can compare the source code to see the comparison. If you have a three-party library in your project that has introduced this
SafeJobIntentService
class, but because you can't use this class of them, and you refer to such as implementation 'com.evernote:android-job:1.4.2'
library, duplicate class found in the module
. If this problem occurs, we can rename the class and follow the above code to deal with it. Hopefully Google will add a solution to this problem in future libraries.
JSON has three methods for parsing data