Android: java.util.concurrent.ThreadPoolExecutor

В моем приложении есть RecyclerView с тоннами изображений в нем. Изображения загружаются как пользовательские прокрутки RecyclerView с помощью этого кода:

  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,url); else loader.execute(url); 

К сожалению, иногда, когда пользователь быстро прокручивает эту ошибку, происходит:

 Task android.os.AsyncTask$3@73f1d84 rejected from java.util.concurrent.ThreadPoolExecutor@8f5f96d[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 279] 

Есть ли способ определить, заполнен ли пул PoolExecutor и пропускать загрузку изображения?

Класс полного изображения:

 public class Image extends ImageView { private AsyncTask<String,Integer,Bitmap> loader; public Image(Context context) { super(context); this.setScaleType(ScaleType.FIT_XY); } public Image(Context context, AttributeSet attrs) { super(context, attrs); this.setScaleType(ScaleType.FIT_XY); } public void loadURL(String url) { if(loader!=null) loader.cancel(true); loader=new AsyncTask<String, Integer, Bitmap>() { @Override protected Bitmap doInBackground(String... params) { URL url = null; byte[] bytes = null; HttpURLConnection connection=null; try { url = new URL(params[0]); connection=(HttpURLConnection) url.openConnection(); connection.setRequestProperty("Connection", "close"); connection.setRequestMethod("GET"); connection.setUseCaches(true); InputStream is = null; is=connection.getInputStream(); bytes = IOUtils.toByteArray(is); } catch (MalformedURLException e) { e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (connection!=null) connection.disconnect(); Bitmap res=null; if(!isCancelled() && bytes!=null) res=BitmapFactory.decodeByteArray(bytes,0,bytes.length); return res; } @Override protected void onPostExecute(Bitmap res) { if(res!=null) { setImageBitmap(res); _animate(); } } }; if (this.getDrawable()!=null) { Bitmap bmp=((BitmapDrawable) this.getDrawable()).getBitmap(); this.setAnimation(null); if (bmp!=null) { bmp.recycle(); //Log.d("image","recycled"); } this.setImageBitmap(null); } /* ThreadPoolExecutor e =(ThreadPoolExecutor) Executors.newFixedThreadPool(9); Log.d("pool size",e.getActiveCount()+"/"+e.getMaximumPoolSize()); if (e.getActiveCount() == e.getMaximumPoolSize()) { } */ //start loading if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); else loader.execute(url); } private void _animate() { ValueAnimator bgAnim= ValueAnimator.ofObject(new IntEvaluator(),0,255); bgAnim.setDuration(500); bgAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Image.this.getDrawable().setAlpha((int) (animation.getAnimatedValue())); } }); bgAnim.start(); } 

}

Solutions Collecting From Web of "Android: java.util.concurrent.ThreadPoolExecutor"

Я отвечаю на это раньше ( здесь , здесь , здесь и здесь и, возможно, другие), и я снова отвечаю за него: не пытайтесь изобрести колесо!

Загрузка изображений / кэширование – очень сложная задача в Android, и многие хорошие разработчики уже это сделали. Threading – это только одна из проблем, но я вижу, что из вашего кода у вас есть утечка памяти, нет кэширования, поэтому вы снова загружаете изображения, если прокрутите назад до него, HttpURLConnection – это дрянной сетевой уровень.

Итак, способ решить эту проблему (IMHO) – это просто повторить работу других разработчиков. Хорошими примерами библиотек, которые вы должны учитывать, являются:

Пикассо – мой любимый, поэтому для его использования нужно просто позвонить:

 Picasso.with(context).load(url).into(imgView); 

И все это будет для вас.

Вы можете проверить, равен ли количество активных потоков равным максимальному размеру пула потоков, а затем пул потоков заполнен, используя этот

 ThreadPoolExecutor e =(ThreadPoolExecutor)Executors.newFixedThreadPool(totalnofthreads); if (e.getActiveCount() == e.getMaximumPoolSize()) { } 

Чтобы определить, быстро ли прокручивается пользователь, вы можете использовать onFlingListener()

 recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() { @Override public boolean onFling(int velocityX, int velocityY) { isFlinging = checkFlinging(velocityY); if (isFlinging) { //Stop image loading here } return false; } }); private boolean checkFlinging(int velocityY) { return (velocityY < 0 && velocityY < -RECYCLER_VIEW_FLING_VELOCITY) || (velocityY > 0 && velocityY > RECYCLER_VIEW_FLING_VELOCITY); } 

Позвольте мне немного пояснить, speedY, потому что я использую recyclerView с вертикальной прокруткой (для горизонтальной прокрутки просто измените этот параметр на velocityX ), RECYCLER_VIEW_FLING_VELOCITY – ваша скорость RECYCLER_VIEW_FLING_VELOCITY = 7000 , для меня RECYCLER_VIEW_FLING_VELOCITY = 7000 .

Я просто переделал, я могу упаковать код загрузки с помощью try / catch:

 try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); else loader.execute(url); } catch (RejectedExecutionException e){ e.printStackTrace(); } 

Похоже, это было бы необязательным решением.

Вместо использования AsyncTask.THREAD_POOL_EXECUTOR вы можете использовать свой собственный экземпляр Executor с RejectedExecutionHandler , например:

 private final Executor mExecutor = new ThreadPoolExecutor(0, 8, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(16), new ThreadPoolExecutor.DiscardPolicy()); 

Это создаст Исполнителя, который будет работать не более чем из 8 потоков, сохраняет 16 задач в очереди и бросает любые задачи выше этих ограничений ( DiscardPolicy – это предопределенный RejectedExecutionHandler который делает именно это). Затем вы можете передать его executeOnExecutor() .

Кроме того, вы можете захотеть, чтобы изображения в конечном итоге загрузились. В этом случае вы можете использовать Executor с неограниченной (т.е. безграничной) очередью. Он никогда не будет вызывать исключение RejectedExecutionException. Класс Утилитов Executors имеет хороший заводский метод для создания таких:

 private final Executor mExecutor = Executors.newFixedThreadPool(8); 

Когда ImageView отсоединен от окна, отмените соответствующую задачу загрузки URL-адреса:

 public class Image extends ImageView { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(loader!=null) loader.cancel(true); } } 

Прежде всего, почему вы все еще используете AsyncTask? Исключение ThreadPool приходит, потому что, в то время как ваша прокрутка быстро адаптера пытается установить изображение в позицию, которая больше не доступна, как правило, чтобы остановить эту проблему, вы отключили бы рециркуляцию, но это только сделало бы ваш список медленным при обработке большого набора данных , Поэтому я советую вам использовать залп для загрузки изображений, его легко реализовать и легко обрабатывать кеширование.

  <com.android.volley.toolbox.NetworkImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/mainImage" android:scaleType="centerCrop" android:adjustViewBounds="true" android:maxHeight="270dp" /> 

Используйте выше вместо вашего изображения и создайте класс volleySingleton для обработки всех сетевых запросов

  public class VolleySingleton { private static VolleySingleton sInstance = null; private RequestQueue mRequestQueue; private ImageLoader imageLoader; private VolleySingleton(){ mRequestQueue = Volley.newRequestQueue(Application.getAppContext()); imageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() { private final LruCache<String, Bitmap> cache = new LruCache<String, Bitmap>(200); @Override public Bitmap getBitmap(String url) { return cache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { cache.put(url, bitmap); } }); } public static VolleySingleton getsInstance(){ if(sInstance == null){ sInstance = new VolleySingleton(); } return sInstance; } public RequestQueue getmRequestQueue(){ return mRequestQueue; } public ImageLoader getImageLoader() { return imageLoader; } } 

Получите экземпляр вашего одноэлементного класса, затем добавьте его в изображениеView и ваше добро для перехода

  imageLoader = VolleySingleton.getsInstance().getImageLoader(); networkImageVeiw.setImageUrl(imageUrl, imageLoader); 

Создайте класс, расширяющий android.app.Application, чтобы вы могли получить контекст в классе volleySingleton

  public class Application extends android.app.Application { private static Application sInstance; public static Application getsInstance() { return sInstance; } public static Context getAppContext() { return sInstance.getApplicationContext(); } public static AppEventsLogger logger(){ return logger; } @Override public void onCreate() { super.onCreate(); sInstance = this; } } 

Не забудьте зайти в свой манифест.xml и добавить атрибут name в тег приложения как имя вашего класса приложения, которое вы просто расширили

  <application android:name="com.example.ApplicationClass" 

Вот ссылка, чтобы получить инструкции по установке волейбола и некоторые полезные советы