Setting up the Android Google Volley ImageLoader for NetworkImageView
Google Volley is awesome. If you're an Android developer and you've done AsyncTask and HttpClient stuff (and there is no avoiding it), prepare for a huge relief once you get up and running with Volley. All of the leg work has been handled for you when it comes to managing the threads, dealing with a JSON response, handling errors, and way more.
Using NetworkImageView
Included with the library is an extremely useful object called NetworkImageView. NetworkImageView is meant to be a direct replacement for the standard ImageView in scenarios when your image resource is coming from a network location (i.e. URL) rather than a static resource. In your layout file, it's as simple as changing the element from ImageView to NetworkImageView, for example:
<ImageView android:id="@+id/twitter_avatar" android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:layout_marginRight="6dip" />
Becomes
<com.android.volley.toolbox.NetworkImageView android:id="@+id/twitter_avatar" android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:layout_marginRight="6dip" />
The next step is to populate your element programatically using the URL of the image. This is straight forward as well, at least at first glance:
NetworkImageView avatar = (NetworkImageView)view.findViewById(R.id.twitter_avatar); avatar.setImageUrl("http://someurl.com/image.png",mImageLoader);
And that's where just about every example on the web leaves it. But what about that mImageLoader variable?
Setting up the ImageLoader
It's more complicated than it appears. The ImageLoader class is actually responsible for setting up caching on the device, and initializing the object isn't trivial. To get going with the ImageLoader, start by declaring private variables for use in your class:
private RequestQueue mRequestQueue; private ImageLoader mImageLoader;
Then in your constructor or OnCreate() method, initialize the objects using the following:
mRequestQueue = Volley.newRequestQueue(context); mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() { private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10); public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } public Bitmap getBitmap(String url) { return mCache.get(url); } });
Now you're free to use the magical NetworkImageView.setImageUrl() method as intended:
NetworkImageView avatar = (NetworkImageView)view.findViewById(R.id.twitter_avatar); avatar.setImageUrl("http://someurl.com/image.png",mImageLoader);
Singleton Pattern
Google recommends that you use a common RequestQueue object for your app in the form of a Singleton. This is also something that is glossed over in examples, so have another one on us.
import android.content.Context; package com.company.myapplication; import android.content.Context; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; public class VolleySingleton { private static VolleySingleton mInstance = null; private RequestQueue mRequestQueue; private ImageLoader mImageLoader; private VolleySingleton(){ mRequestQueue = Volley.newRequestQueue(MyApplication.getAppContext()); mImageLoader = new ImageLoader(this.mRequestQueue, new ImageLoader.ImageCache() { private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10); public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } public Bitmap getBitmap(String url) { return mCache.get(url); } }); } public static VolleySingleton getInstance(){ if(mInstance == null){ mInstance = new VolleySingleton(); } return mInstance; } public RequestQueue getRequestQueue(){ return this.mRequestQueue; } public ImageLoader getImageLoader(){ return this.mImageLoader; } }
You'll need to implement a static application context in a class which extends Application. Then, update line 15 of VolleySingleton.java to call your static context getter method. An example of what this application class might look like can be found here: MyApplication.java
Using the Singleton is as simple as:
RequestQueue queue = VolleySingleton.getInstance().getRequestQueue(); //or mImageLoader = VolleySingleton.getInstance().getImageLoader();
Download
Get the singleton and instructions from our GitHub repo here https://github.com/CypressNorth/Volley-Singleton
31 Comments
Leave a Reply
Meet the Author
Matthew Mombrea
Matt is our Chief Technology Officer and one of the founders of our agency. He started Cypress North in 2010 with Greg Finn, and now leads our Buffalo office. As the head of our development team, Matt oversees all of our technical strategy and software and systems design efforts.
With more than 19 years of software engineering experience, Matt has the knowledge and expertise to help our clients find solutions that will solve their problems and help them reach their goals. He is dedicated to doing things the right way and finding the right custom solution for each client, all while accounting for long-term maintainability and technical debt.
Matt is a Buffalo native and graduated from St. Bonaventure University, where he studied computer science.
When he’s not at work, Matt enjoys spending time with his kids and his dog. He also likes to golf, snowboard, and roast coffee.
Dumping volley for Picasso and retrofit combo.
How is it that this article has no comments... this code is epic. It works perfectly. It's so hard to find good documentation on Volley and the whole image loading with recycled views is a huge hurdle for beginners. I can't thank you enough!
Thank you for this. This cleared up a few things for me.
Now I will use Volley in my application.
This is the perfect example I have found about ImageView Loader on the Internet till date.. Thanks for sharing
Thank you so much for this post, I finally managed to understand the whole concept!
Great post, Thanks 😉
Thanks for this great Article, I would recomend you to store the RequestQueue in Application or MainActivity and acces it from there via a getter. THANK YOU VERY MUCH!
You're welcome. I think that the singleton pattern is the recommended way to handle the RequestQueue.
http://stackoverflow.com/questions/16682595/android-volley-imageloader-bitmaplrucache-parameter
check this one to make it perfect , and allow the size of cache
Also how to set a place holder till the image is loaded and is there is any listener for checking whether the image is set to Image view . Consider a condition in which a progress indicator is shown until image is loded ti view.
Really brief and concise, but great !! You saved me from a good headache here.
PS: It would be great if you post the files for non advanced users, it used to hep me a lot.
Fabio,
Added a link to our GitHub repo with the file
https://github.com/CypressNorth/Volley-Singleton
Thanks for the post. Singleton Pattern seems to make sense. Is it not common for networkimageview to face OutOfMemory errors?
I haven't experienced any out of memory issues to date but I can't say I know for sure
The problem I see using a Singleton here is that the constructor takes one argument (a Context object). An object requiring a parameter in the constructor cannot be a Singleton per se. Let's say we have ActivityA and ActivityB. ActivityA calls VolleySingleton.getInstance(this). Later, ActivityB calls VolleySingleton.getInstance(this). It will return the instance created by ActivityA since mInstance != null. What if the context is not available anymore (i.e. ActivityA has been destroyed). Does it crash?
Jean-Francois,
Very good point. I'll have to fire up the debugger to see exactly what's going on in that scenario. I'm using this VolleySingleton in several Activities and haven't experienced any issues yet. I suspect (but it's just a guess at this point) that each time a new Activity is loaded the previous Activity is unloaded, along with the instance of the singleton. In that way, the singleton would be scoped to the life of that activity. I have no idea if that's true or not but when I have a moment I'll step through an example and figure it out. If it does indeed crash between Activities, I'll be sure to update this post and the Github. Thank you!
I don't think that the singleton would be scoped to the lifecycle of an Activity. To resolve this I thought about creating the singleton in an Application object (https://developer.android.com/reference/android/app/Application.html). However, reading the documentation about this class lead me to the solution:
"If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton."
From what I understand passing any Context would do, but I think that it would even be better if the constructor and getInstance would not require any Context and use Context.getApplicationContext() internally in the constructor. What do you think?
I think you're right, and that sounds like a good solution. Looking at the Volley newRequestQueue method (which requires the context), the comment for the context is "context A {@link Context} to use for creating the cache dir."
If it's only being used to get/create the cache directory, the application context seems appropriate.
Totally agree.
The Github repo has been updated with your contribution, thanks! I tested out the situation and, while the different contexts didn't crash the app, they also were generating different cache files, effectively limiting the loader cache to a single Activity rather than the whole application. This solution is much better.
It's nice article .
The best way to maintain volley core objects and request queue is, making them global by creating a singleton class which extends Application object and create your singleton implementation within this class .
The way volley works Its totally disaster.If You are working with high resolution image and you are downloading 500 hundred images,It never work.OutOfMemory error does not handled by volley.
Ashish,
That is not a problem with volley, it's a problem with the strategy. All mobile applications have a limited amount of memory to work with which is governed by the OS. You need to be careful with the size and number of images that you're loading into memory and displaying at once. No library will help you load 500 high-res images onto a mobile app at once.
Has anyone used the ImageLoader for Https requests. I need to put some basic authorization into my requests but don't see a simple way to do that with the ImageLoader
hi, Matthew Mombrea, its a fantastic article but I have a few doubts, if the ImageCache works as intended , why are people discussing issues and shortcomings about the ImageCaching here http://stackoverflow.com/questions/21848749/volley-image-caching and infact going to the extent of using a custom DiskBasedCache made by Jake Wharton, the founder of sherlockactionbar, does the LruCache work inside an adapter such as a BaseAdapter's getView or RecyclerView.Adapter's onBindViewHolder , I have used other libraries in the past such as Picasso and UniversalImageLoader to manage images downloaded from a url inside a ListView or RecyclerView, however I feel Volley is missing something, can you please enlighten me on it, thanks in advance
Vlad,
The reason for the confusion is the lack of documentation on how the cache is supposed to be used with Volley. In that StackOverflow thread, the question shows the person using the LruCache within the OnCreate method of a fragment. It doesn't look as if he's creating a VolleySingleton using a single context for cache, or even the fragment for that matter. If you don't use a singleton and you don't use a common context, the cache won't work as you expect.
Even using a singleton, you need to be careful about the context you give it. If you look back in this comment thread, Jean-François Cartier caught a mistake in my code related to the cache context. It's since been updated and works as you'd expect.
Here's the link to the repo showing how to use the singleton pattern with volley + LruCache:
https://github.com/CypressNorth/Volley-Singleton
Nice post on volley.. The discussions help a lot..
Nice post!
Since this is a singleton pattern, I think it is necessary to make sure it is thread safe.:)
Very helpful
Thank you!
PERFECT!!! thank you so much