安卓: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(); } 

}

我在之前( 在这里 , 在这里 , 在 这里 ,也可能在其他地方)回答这个问题,我再次回答你:不要试图重新发明轮子!

图像加载/caching是Android中非常复杂的任务,许多优秀的开发人员已经这样做了。 线程只是其中一个问题,但是我可以从你的代码中看到你有一个内存泄漏,没有caching,所以如果再次滚动它,你将重新下载图像, HttpURLConnection是一个糟糕的networking层。

所以,解决这个问题的方法(恕我直言)就是重用其他开发者所做的工作。 你应该考虑的图书馆的好例子是:

毕加索是我最喜欢的,所以要使用它,你只需要简单地调用:

 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); } 

让我解释一下,因为我用垂直滚动的回滚视图(水平滚动只是改变这个参数为velocityX ),RECYCLER_VIEW_FLING_VELOCITY – 你的投掷速度, RECYCLER_VIEW_FLING_VELOCITY = 7000

我只是relized我可以用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(); } 

看起来这将是可选的解决scheme。

而不是使用AsyncTask.THREAD_POOL_EXECUTOR您可以使用自己的Executor实例与RejectedExecutionHandler ,例如:

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

这将创build一个运行最多8个线程的Executor,在队列中保留16个任务,并将任何任务放在这些限制之上( DiscardPolicy是一个预定义的RejectedExecutionHandler ,完全是这样做的)。 然后你可以把它传递给executeOnExecutor()

或者,您可能希望图像最终加载。 在这种情况下,你可以使用一个无限(即无限制)队列的执行程序。 它永远不会抛出一个RejectedExecutionException。 执行程序实用程序类有一些很好的工厂方法来创build这些:

 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? ThreadPoolexception即将到来,因为当您快速滚动适配器试图将图像设置到不再可用的位置通常停止此问题,您将禁用回收,但这只会使您的列表缓慢处理大量的数据。 所以我build议你使用凌云图像加载其易于实现和简单地处理caching。

  <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" /> 

使用上面的代替你的imageview和创buildvolleySingleton类来处理所有的networking请求

  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; } } 

获取你的单例类的一个实例,然后将其添加到imageView和你的好去

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

创build一个扩展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; } } 

不要忘记去你的manifest.xml,并将name属性添加到应用程序标签中,以作为您刚扩展的应用程序类的名称

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

这里是一个链接,获取安装凌空和一些有用的技巧排球库这里的指示