Избежать исключения 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);