Избежать исключения RejectedExecutionException в Android 4.4, когда приложение использует список

В Android 4.4, похоже, происходит изменение кода, который вызывает загрузку значков списков с помощью AsyncTasks. В результате многие мои пользователи на Android 4.4 получают RejectedExecutionException, поскольку предел размера очереди превышен.

Умный пользователь в Код Google открыл это и объяснил это следующим образом:

ResolverActivity будет вызывать исключение RejectedExecutionException на Android 4.4.

Я просмотрел код последней ResolverActivity и заметил, что в методе ResolveListAdapter.bindView используется новый LoadIconTask(). execute (info), это должно быть основной причиной. LoadIconTask является подклассом AsyncTask, слишком много запусков AsyncTask вызывает RejectedExecutionException.

Изменение ResolverActivity можно найти в репозитории Android GitHub.

В настоящее время мое приложение имеет 82 трассировки стека для RejectedExecutionException, все из которых предназначены для Android 4.4. Пример начала стека:

java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][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

Проблема заключается в том, что различные исполнители, используемые 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 "чистое" решение)

Используйте отдельный APK с targetSdkVersion > 12 и более высокий код версии, поэтому это предпочтительнее для HONEYCOMB_MR2 и более поздних версий Android. Это заставит AsyncTask использовать ThreadPool.SERIAL_EXECUTOR на HONEYCOMB_MR2 и более поздней версии Android.

Решение 2 (AKA грязный хак)

Просто создайте AsyncTask.SERIAL_EXECUTOR по умолчанию с помощью Reflection.

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