Thursday, April 3, 2014

How to create an ImageLoader for a huge amount of images on Android

Hi there!

During development with a huge amount of images, most of all image loaders available out there, does not display(caches) the images smoothlty. For this reason, i have tryed to put all the code "pieces" i found in the internet together to get a almost perfect image loader for my application. The result is shown bellow. It works very well for me. It may be also interesting for other developers. Here is my solution:

The Loader interface

/**
 * Drawable loader used to create and cache thumbnails while presenting it in a gallery preview.
 * IMPORTANT: instantiate it in your main activity like this:
 * 

 * protected void onCreate(final Bundle savedInstanceState) {
 *       super.onCreate(savedInstanceState);
 *       ...
 *       DrawableLoader.getInstance(this);
 *       ...
 *   }
 * 
 * 
* @author Ricardo Ferreira * @version 1.0 * @since 03/04/2014 */ public interface Loader { /** * Clears all instance data and stops running threads */ public void Reset() ; /** * Loads a drawable into an image. Call this method inside of getView(...) from your concrete * implementation of BaseAdapter. *
     * public View getView(int position, View convertView, ViewGroup parent){
     * ...
     * String url = PATH_FILE + pathImage;// path to your storage like "/storage/..."
     * ImageView imageView = (ImageView)holder.imgView;
     * final Drawable placeholder = mContext.getResources().getDrawable(R.drawable.placeholder);
     * DrawableLoader.getInstance(mContext).loadDrawable(url, imageView, placeholder);
     * ...
     * }
     * 
* @param url file path to the storage location * @param imageView the image to be presented in a list view or grid view * @param placeholder the place holder image to be shown, while the loading is running. */ public void loadDrawable(final String url, final ImageView imageView, Drawable placeholder); }

The Loader implementation

/**
 * Drawable loader used to create and cache thumbnails while presenting it in a gallery preview.
 * IMPORTANT: instantiate it in your main activity like this:
 * 

 * protected void onCreate(final Bundle savedInstanceState) {
 *       super.onCreate(savedInstanceState);
 *       ...
 *       DrawableLoader.getInstance(this);
 *       ...
 *   }
 * 
 * 
* @author Ricardo Ferreira * @version 1.0 * @since 03/04/2014 */ @SuppressLint("HandlerLeak") public class DrawableLoader implements Loader{ private final Map < String, SoftReference < Drawable > > mCache = new HashMap < String, SoftReference < Drawable > >(); private final LinkedList < Drawable > mChacheController = new LinkedList < Drawable > (); private ExecutorService mThreadPool; private final Map < ImageView, String > mImageViews = Collections.synchronizedMap(new WeakHashMap < ImageView, String >()); public final static int MAX_CACHE_SIZE = 1024; public final int THREAD_POOL_SIZE = 5; private Context mContext; private static Loader loader; // SINGLETON private DrawableLoader(Context context) { mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE); mContext=context; } public static Loader getInstance(Context ctx){ if(loader==null){ loader = new DrawableLoader(ctx); } return loader; } public void Reset() { ExecutorService oldThreadPool = mThreadPool; mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE); oldThreadPool.shutdownNow(); mChacheController.clear(); mCache.clear(); mImageViews.clear(); } public void loadDrawable(final String url, final ImageView imageView, Drawable placeholder) { mImageViews.put(imageView, url); Drawable drawable = getDrawableFromCache(url); if (drawable != null) { imageView.setImageDrawable(drawable); } else { imageView.setImageDrawable(placeholder); queueJob(url, imageView, placeholder); } } private Drawable getDrawableFromCache(String url) { if (mCache.containsKey(url)) { return mCache.get(url).get(); } return null; } private synchronized void putDrawableInCache(String url, Drawable drawable) { int chacheControllerSize = mChacheController.size(); if (chacheControllerSize > MAX_CACHE_SIZE){ mChacheController.subList(0, MAX_CACHE_SIZE / 2).clear(); } mChacheController.addLast(drawable); mCache.put(url, new SoftReference(drawable)); } private void queueJob(final String url, final ImageView imageView, final Drawable placeholder) { /* Create handler in UI thread. */ final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { String tag = mImageViews.get(imageView); if (tag != null && tag.equals(url)) { if (imageView.isShown()) if (msg.obj != null) { imageView.setImageDrawable((Drawable) msg.obj); } else { imageView.setImageDrawable(placeholder); } } } }; mThreadPool.submit(new Runnable() { @Override public void run() { final Drawable bmp = downloadDrawable(url); // if the view is not visible anymore, //the image will be ready for next time in cache if (imageView.isShown()) { Message message = Message.obtain(); message.obj = bmp; handler.sendMessage(message); } } }); } private Drawable downloadDrawable(String url) { final Bitmap decodeBitmap = ViewHelper.decodeBitmap(url.replaceAll("file://", ""), 100); Drawable drawable = new BitmapDrawable(mContext.getResources(), decodeBitmap); putDrawableInCache(url, drawable); return drawable; } /** pass new URL(urlString) to it, while urlString is the path to the drawable like: * http://www.google.com.br/images/your_image.png */ protected Drawable downloadDrawable(URL url) { try { InputStream is = getInputStream(url); Drawable drawable = Drawable.createFromStream(is, url.getPath()); putDrawableInCache(url.getPath(), drawable); return drawable; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private InputStream getInputStream(URL url) throws MalformedURLException, IOException { //URL url = new URL(urlString); URLConnection connection; connection = url.openConnection(); connection.setUseCaches(true); connection.connect(); InputStream response = connection.getInputStream(); return response; } }

That's all. Hope you like it! ;)

😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO ðŸ˜±ðŸ‘‡

Be sure to read, it will change your life!
Show your work by Austin Kleonhttps://amzn.to/34NVmwx

This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practiceshttps://amzn.to/30WQSm2

Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv

This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp

Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy

😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO ðŸ˜±ðŸ‘†