当app使用列表时,在Android 4.4中避免RejectedExecutionException

时间:2014-06-21 16:25:44

标签: android android-asynctask android-4.4-kitkat threadpoolexecutor

在Android 4.4中,代码似乎发生了变化,导致使用AsyncTasks加载列表图标。结果是Android 4.4上的许多用户获得RejectedExecutionException,因为超出了队列大小限制。

Code Google的聪明用户发现了这一点,并以这种方式解释:

  

ResolverActivity将在Android 4.4上抛出RejectedExecutionException。

     

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

可以在Android GitHub repo找到ResolverActivity更改。

我的应用目前有RejectedExecutionException的82个堆栈跟踪,所有这些都是针对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)
...

有没有办法回避或处理这种变化?

1 个答案:

答案 0 :(得分:21)

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

1)targetSdkVersion< = 12

AsyncTask.execute()使用AsyncTask.THREAD_POOL_EXECUTOR。 AsyncTask.THREAD_POOL_EXECUTOR中的队列限制为128个项目。如果队列已满,则抛出RejectedExecutionException。这就是这里发生的事情

2)targetSdkVersion> 12

AsyncTask使用AsyncTask.SERIAL_EXECUTOR。 AsyncTask.SERIAL_EXECUTOR具有无界队列。所以在这种情况下,永远不会抛出RejectedExecutionException。

解决方案1(AKA" clean"解决方案)

使用单独的APK与targetSdkVersion> 12和更高版本的代码,因此对于HONEYCOMB_MR2和更高版本的Android来说是首选。这将导致AsyncTask在HONEYCOMB_MR2及更高版本的Android上使用ThreadPool.SERIAL_EXECUTOR。

解决方案2(AKA the dirty hack)

使用Reflection。将AsyncTask.SERIAL_EXECUTOR设为默认值。

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