Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android:java.util.concurrent.ThreadPoolExecutor

In my app there is RecyclerView with tons of images in it.Images are loaded as user scrolls RecyclerView with this code:

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

Unfortunately sometimes when user scrolls fast this error occurs:

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]

Is there way to detect if poolExecutor is full and skip image loading?

Whole Image class:

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

}

like image 478
undefined Avatar asked Jun 05 '17 08:06

undefined


3 Answers

I answer that before (here, here, here and here and probably others) and I answer it again for you: Do not try to re-invent the wheel!

Image loading/caching is a very complex task in Android and a lot of good very developers already did that. Threading is just one of the issues, but I can see from your code you have a memory leak, there's no caching so you'll re-download images again if scroll back to it, HttpURLConnection is a crappy network layer.

So, the way to solve this (IMHO) it's just to re-use work done by other developers. Good examples of libraries you should consider for that are:

  • Picasso - https://github.com/square/picasso
  • Glide - https://github.com/bumptech/glide
  • Fresco - https://github.com/facebook/fresco

Picasso is my favorite, so to use it you need to simply call:

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

and it all be handled for you.

like image 174
Budius Avatar answered Oct 06 '22 00:10

Budius


You can check if active threads count is equal to thread pool maximum size then your thread pool is full by using this

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

}
like image 35
Meenu Meena Avatar answered Oct 05 '22 23:10

Meenu Meena


I just relized I can wrap loading code with 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();
}

Looks like this would be optional solution.

like image 37
undefined Avatar answered Oct 06 '22 00:10

undefined