Tag Archives: Source code analysis

[Android Error] java.lang.RuntimeException: An error occurred while executing doInBackground()

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