Avoiding RejectedExecutionException in Android 4.4 when app uses list

安稳与你 提交于 2019-12-17 18:55:26

问题


In Android 4.4 there seems to be a change in the code that causes list icons to be loaded using AsyncTasks. The result is that many of my users on Android 4.4 get RejectedExecutionException since the queue size limit is exceeded.

A clever user at Code Google discovered this, and explained it in this way:

ResolverActivity will throw RejectedExecutionException on Android 4.4.

I viewed the code of latest ResolverActivity and noticed that in ResolveListAdapter.bindView method it is using new LoadIconTask().execute(info), this should be the root cause. LoadIconTask is a subclass of AsyncTask, too many AsyncTask running at the same time will cause RejectedExecutionException.

The ResolverActivity change can be found at the Android GitHub repo.

My app currently has 82 stack traces for RejectedExecutionException, all of which are for Android 4.4. Example start of stack:

java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@41d44580 rejected from java.util.concurrent.ThreadPoolExecutor@41a575c0[Running, pool size = 5, active threads = 5, queued tasks = 128, completed tasks = 140]
 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2011)
 at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:793)
 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1339)
 at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:590)
 at android.os.AsyncTask.execute(AsyncTask.java:535)
 at com.android.internal.app.ResolverActivity$ResolveListAdapter.bindView(ResolverActivity.java:716)
 at com.android.internal.app.ResolverActivity$ResolveListAdapter.getView(ResolverActivity.java:702)
 at android.widget.AbsListView.obtainView(AbsListView.java:2255)
...

Is there any way to sidestep or handle this change?


回答1:


The problem lies with the different Executors that AsyncTask uses depending on targetSdkVersion of the app:

1) targetSdkVersion <= 12

AsyncTask.execute() uses the AsyncTask.THREAD_POOL_EXECUTOR. The queue in AsyncTask.THREAD_POOL_EXECUTOR is limited to 128 items. If the queue is full RejectedExecutionException is thrown. This is what happens here

2) targetSdkVersion > 12

AsyncTask uses the AsyncTask.SERIAL_EXECUTOR. AsyncTask.SERIAL_EXECUTOR has an unbounded queue. So in this scenario RejectedExecutionException is never thrown.

Solution 1 (AKA the "clean" solution)

Use a separate APK with targetSdkVersion > 12 and a higher versionCode so that is preferred for HONEYCOMB_MR2 and later versions of Android. This will cause AsyncTask to use ThreadPool.SERIAL_EXECUTOR on HONEYCOMB_MR2 and later version of Android.

Solution 2 (AKA the dirty hack)

Just make AsyncTask.SERIAL_EXECUTOR the default using Reflection.

AsyncTask.class.getMethod("setDefaultExecutor", Executor.class).invoke(null, AsyncTask.SERIAL_EXECUTOR);


来源:https://stackoverflow.com/questions/24343563/avoiding-rejectedexecutionexception-in-android-4-4-when-app-uses-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!