避免应用程序使用列表时Android 4.4中的RejectedExecutionException

避免应用程序使用列表时Android 4.4中的RejectedExecutionException,android,android-asynctask,android-4.4-kitkat,threadpoolexecutor,Android,Android Asynctask,Android 4.4 Kitkat,Threadpoolexecutor,在Android4.4中,代码中似乎有一个变化,导致使用AsyncTasks加载列表图标。结果是,由于超过了队列大小限制,我在Android 4.4上的许多用户都得到了RejectedExecutionException 一位聪明的用户发现了这一点,并用以下方式解释: ResolveActivity将在Android 4.4上抛出RejectedExecutionException 我查看了最新的ResolveActivity的代码,注意到在ResolveListAdapter.bindView

在Android4.4中,代码中似乎有一个变化,导致使用AsyncTasks加载列表图标。结果是,由于超过了队列大小限制,我在Android 4.4上的许多用户都得到了
RejectedExecutionException

一位聪明的用户发现了这一点,并用以下方式解释:

ResolveActivity将在Android 4.4上抛出RejectedExecutionException

我查看了最新的ResolveActivity的代码,注意到在ResolveListAdapter.bindView方法中,它使用了新的LoadIconTask().execute(info),这应该是根本原因。LoadIconTask是AsyncTask的子类,同时运行太多AsyncTask将导致RejectedExecutionException

可在中找到分辨率更改

我的应用程序目前有82个堆栈跟踪,用于
RejectedExecutionException
,所有这些跟踪都是针对Android 4.4的。堆栈开始的示例:

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)
...

有没有办法回避或处理此更改?

问题在于AsyncTask使用的不同执行器,具体取决于应用程序的targetSdkVersion:

1) targetSdkVersion 12 AsyncTask使用AsyncTask.SERIAL_执行器。AsyncTask.SERIAL_执行器具有无限队列。所以在这个场景中,永远不会抛出RejectedExecutionException

溶液1(也称为“清洁”溶液) 使用targetSdkVersion>12的单独APK和更高版本的代码,这是HONEYCOMB_MR2和更高版本Android的首选。这将导致AsyncTask在HONEYCOMB_MR2和更高版本的Android上使用ThreadPool.SERIAL_EXECUTOR

解决方案2(又称肮脏黑客) 只需使用反射将AsyncTask.SERIAL_执行器设置为默认值

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

非常感谢。您知道早期版本使用POOL vs SERIAL的原因吗?一些手机强制串行通信会中断吗?此外,前后切换的理由见文档。基本上,使用SERIAL_EXECUTOR是更安全的默认设置,因为这会减少重现线程问题的难度,所以他们改用它。不,我不认为切换到SERIAL_EXECUTOR会破坏任何东西。既然SERIAL_EXECUTOR是在API级别11中添加的,“hack”只适用于Minsdk版本11或12(或更高版本,但无论如何都要使用“clean”),我的假设正确吗?虽然您的“干净”解决方案适用于小于11的版本,但如果API太低,实际上不会改变任何内容。是的,这是正确的。您最初的错误报告是针对在KitKat上运行的应用程序的。你对其他Android版本的RejectedExecutionException也有问题吗?没有,只是澄清一下黑客什么时候有用。设置targetSdkVersion是一个可靠的选项。非常感谢。