Android: Consuming a Remote JSON API with Volley
In the previous tutorial, you learned how to use Volley's built-in networking tools to quickly download a network resource and display the results.
Volley is a networking library that removes lots of boiler-plate code when dealing with handling data across a network. It provides a number of helpful classes to simplify downloading and parsing data and images, and lets you concentrate on the details of your app rather than dealing with low-level networking technologies.
In this post, we'll start a new project and go further into Volley, using the built-in JSONObjectRequest
and ImageRequest
classes to make use of the remote data. We'll also use Volley's custom NetworkImageView
widget to easily (and safely) load a remote image into our UI.

Create a new Android Studio Project
As in the previous tutorial, we'll use Android Studio to build the project. Once installed, create a new project for our app using the Android New Project setup:
→ Create a new project in Android Studio with the following settings:
- Open Android Studio and choose Start a New Android Studio Project
- Name your new app
VolleyApp2
under theexample.com
company name (this will automatically set your package name tocom.example.volleyapps2
). - Choose Phone and Tablet and API 21: Android 5.0 (Lollipop) for the Minimum SDK. This will automatically configure your app to use the Android Support Libraries to support older devices.
- Click Next and then Basic Activity. Later we'll add a
ListView
to browse the remote data. - Name your activity
ImagesActivity
, and layoutactivity_images
. - Check Use a Fragment.
- Finally, click Finish to create your new project.
Once your project has been created and Gradle has updated, hit Build->Run (Ctrl+R) to test the new app. You should see the standard Hello World! message on your device or emulator.
Add Volley Dependency to your Gradle File
Volley is now officially available via Gradle from the Android Open Source Project (this tutorial previously used this mirror, which is now deprecated).
→ Add the Volley dependency to your module's build.gradle
file:
// /app/build.gradle
// ...
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
// ...
compile 'com.android.volley:volley:1.0.0'
}
Android Studio will notify you that it needs to Sync the Gradle config. This ensures that your project's configuration matches that of Gradle within Android Studio. Synchronising will also download the Volley as a dependency, making its classes available within our app.
If you don't see this notification, choose Build->Rebuild Project... to refresh and sync the project.
Create a Data Model
We'll set up a simple UI to hold our list of images and their titles. This will use the standard Adapter
pattern that is common throughout Android.
Let's start by creating a data model to represent the downloaded data. When consuming a web service, you'll likely want to store a local cache of the downloaded data for using offline. For Android apps, this is likely to be a SQLite database to persist the database.
For this tutorial, though, we'll just cache the data - a list of image URLs and
their titles - into an ArrayList
of simple ImageRecord
objects.
→ Create a new class, ImageRecord
with fields for the image URL and title:
// /app/src/main/java/com/example/volleyapp2/ImageRecord.java
public class ImageRecord {
private String url;
private String title;
public ImageRecord(String url, String title) {
this.url = url;
this.title = title;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
}
→ Create a new class, ImageRecordsAdapter
:
This will hold our list of ImageRecords
and adapt them to
ImagesActivityFragment
UI.
// /app/src/main/java/com/example/volleyapp2/ImageRecordsAdapter.java
public class ImageRecordsAdapter extends ArrayAdapter<ImageRecord> {
public ImageRecordsAdapter(Context context) {
super(context, R.layout.image_list_item);
}
}
We haven't yet created the image_list_item
layout. Android Studio will notice this and warn us to create the resource. Do that now using the following simple layout.
→ Create a new layout file for image_list_item
:
<!-- /app/src/main/res/layout/image_list_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.android.volley.toolbox.NetworkImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
tools:src="@mipmap/ic_launcher"
android:id="@+id/image1"/>
<TextView
android:padding="8dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:textAppearanceLarge"
tools:text="Image Name"
android:id="@+id/text1"/>
</LinearLayout>
Notice the use of com.android.volley.toolbox.NetworkImageView
instead of the normal ImageView
widget. NetworkImageView
is a special widget provided by Volley that adds a simple setImageUrl
method.
Volley will automatically handle downloading remote images into the NetworkImageView
for us, cancelling any requests in progress if the NetworkImageView
is scrolled off the screen.
→ Add a new ImageRecordsAdapter
field (mAdapter
) inside ImagesActivityFragment
:
You'll also initialise it in the fragment's onActivityCreated
method.
// /app/src/main/java/com/example/volleyapp2/ImagesActivity.java
// ...
public static class ImagesActivityFragment extends Fragment {
private ImageRecordsAdapter mAdapter;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = new ImageRecordsAdapter(getActivity());
ListView listView = (ListView) getView().findViewById(R.id.list1);
listView.setAdapter(mAdapter);
}
}
→ Open the fragment's layout and remove the placeholder TextView
, replacing it with a ListView
to display the list of images as they are loaded:
<!-- /app/src/main/res/layout/fragment_image.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ImagesActivity$PlaceholderFragment">
<ListView
android:id="@+id/list1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Running the app, you should now see a blank screen. It's time to fetch some remote data using Volley
and populate our list!
Fetch a remote JSON feed using Volley
We'll use Volley in the same way as before to grab a JSON feed. This feed contains a list of images and their titles in the following format:
{
'images': [
{
'url': "http://assets.example.com/image1.jpg",
'title': "Sunset"
},
{
'url': "http://assets.example.com/image2.jpg",
'title': "Beach"
},
// ...
]
}
Start by extending the Android Application
class and adding a global RequestQueue
field (see the previous tutorial for more details on this).
→ Create a new VolleyApplication
class:
// /app/src/main/java/com/example/volleyapp2/VolleyApplication.java
public class VolleyApplication extends Application {
private static VolleyApplication sInstance;
private RequestQueue mRequestQueue;
@Override
public void onCreate() {
super.onCreate();
mRequestQueue = Volley.newRequestQueue(this);
sInstance = this;
}
public synchronized static VolleyApplication getInstance() {
return sInstance;
}
public RequestQueue getRequestQueue() {
return mRequestQueue;
}
}
→ Set the new VolleyApplication
class in your app's AndroidManifest.xml
file:
Now is also a good time to add the android.permission.INTERNET
permission,
so our app can download the remote JSON file.
<!-- /app/main/AndroidManifest.xml -->
<!-- ... -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".VolleyApplication"
...>
</application>
With an accessible, global RequestQueue
ready to receive Volley requests, we can now add a new private method fetch
to ImagesActivityFragment
.
→ Add the following fetch()
method:
// /app/src/main/java/com/example/volleyapp2/ImagesActivity.java
// ...
public static class ImagesActivityFragment extends Fragment {
// ...
private void fetch() {
JsonObjectRequest request = new JsonObjectRequest(
"http://cblunt.github.io/blog-android-volley/response2.json",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
// TODO: Parse the JSON
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getActivity(), "Unable to fetch data: " + volleyError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
VolleyApplication.getInstance().getRequestQueue().add(request);
}
}
→ Add a call to fetch()
at the end of onActivityCreated
:
// /app/src/main/java/com/example/volleyapp2/ImagesActivity.java
// ...
public static class ImagesActivityFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
mAdapter = new ImageRecordsAdapter(getActivity());
ListView listView = (ListView) getView().findViewById(R.id.list1);
listView.setAdapter(mAdapter);
fetch();
}
Parsing the Data
The above code sends a remote request (asychronously) via Volley and calls back with a response, either a successfully parsed JSONObject
from the response, or an error (such as a 404).
In our onResponse
handler, we'll forward the parsed JSONObject
onto a new method, parse
which will convert the JSON feed into a list of ImageRecord
s.
→ Add the parse()
method to ImagesActivityFragment
:
This method to loop through the array of images in the JSON feed, and create a
new ImageRecord
for each one.
// /app/src/main/java/com/example/volleyapp2/ImagesActivity.java
// ...
public static class ImagesActivityFragment extends Fragment {
// ...
private List<ImageRecord> parse(JSONObject json) throws JSONException {
ArrayList<ImageRecord> records = new ArrayList<>();
JSONArray jsonImages = json.getJSONArray("images");
for(int i =0; i < jsonImages.length(); i++) {
JSONObject jsonImage = jsonImages.getJSONObject(i);
String title = jsonImage.getString("title");
String url = jsonImage.getString("url");
ImageRecord record = new ImageRecord(url, title);
records.add(record);
}
return records;
}
}
→ Update the onResponse()
handler to call our new parse()
method
and update the ArrayAdapter
:
// /app/src/main/java/com/example/volleyapp2/ImagesActivity.java
// ...
public static class ImagesActivityFragment extends Fragment {
// ...
private void fetch() {
JsonObjectRequest request = new JsonObjectRequest(
"http://cblunt.github.io/blog-android-volley-2/response.json",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
try {
List<ImageRecord> imageRecords = parse(jsonObject);
mAdapter.swapImageRecords(imageRecords);
}
catch(JSONException e) {
Toast.makeText(getActivity(), "Unable to parse data: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
// ...
}
Next, we need to add the swapImageRecords()
method to ImageRecordsAdapter
.
This simply clears the existing data, adds all the new records, and notifies the
adapter that the underlying data has been updated.
→ Add the ImageRecords()
method:
// /app/src/main/java/com/example/volleyapp2/ImageRecordsAdapter.java
public void swapImageRecords(List<ImageRecord> objects) {
clear();
for(ImageRecord object : objects) {
add(object);
}
notifyDataSetChanged();
}
Before the app can be run, we need to override ImageRecordAdapter
's
getView()
method to specify how the data should be adapted to the UI.
→ Override getView
and inflate the image_list_item
layout:
// /app/src/main/java/com/example/volleyapp2/ImageRecordsAdapter.java
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.image_list_item, parent, false);
}
// NOTE: You would normally use the ViewHolder pattern here
NetworkImageView imageView = (NetworkImageView) convertView.findViewById(R.id.image1);
TextView textView = (TextView) convertView.findViewById(R.id.text1);
ImageRecord imageRecord = getItem(position);
imageView.setImageUrl(imageRecord.getUrl(), mImageLoader);
textView.setText(imageRecord.getTitle());
return convertView;
}
Notice the use of setImageUrl
to load an image directly into the NetworkImageView
. This is provided by Volley.
Just as with the JSONObjectRequest
, we need to let Volley know which
RequestQueue
should be used to manage the downloaded images.
For images, though, Volley provides another layer of abstraction - the
ImageLoader
class. This takes a RequestQueue
and an object implementing the
ImageLoader.ImageCache
interface.
The ImageCache
lets us store downloaded images in memory, only triggering a
network download if image isn't present in the cache.
Caching
Unfortunately, Volley doesn't provide a built-in cache, so we'll need
to write our own. Fortunately, this is relatively simple using the LruCache
(least—recently used) class provided by Android.
→ Create a new class, BitmapLruCache
, with the following code:
// /app/src/main/java/com/example/volleyapp2/BitmapLruCache.java
public class BitmapLruCache extends LruCache<String, Bitmap>
implements ImageLoader.ImageCache {
static int getMaxCacheSize() {
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
return maxMemory / 8;
}
public BitmapLruCache() {
super(getMaxCacheSize());
}
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
}
With a simple in-memory cache ready to use, we can now create an new instance
of ImageLoader
for our adapter to use.
→ Add the following code to create an instance of ImageLoader
in the
adapter's constructor:
// /app/src/main/java/com/example/volleyapp2/ImageRecordsAdapter.java
public class ImageRecordsAdapter extends ArrayAdapter<ImageRecord> {
private final ImageLoader mImageLoader;
public ImageRecordsAdapter(Context context) {
super(context, R.layout.image_list_item);
mImageLoader = new ImageLoader(VolleyApplication.getInstance().getRequestQueue(), new BitmapLruCache());
}
// ...
}
With that, your app is now ready to download and parse downloaded data, and any external images in the feed. The NetworkImageView
(and ImageLoader
) will automatically retrieve and display the images.
→ Run your app, and you will be presented with the results of the parsed feed:

Consuming Remote JSON APIs
You have successfully used Volley to consume a simple JSON API. Although there's lots more that can be done (e.g. disk caching, locally caching and synchronising the retrieved data using SQLite, etc.), this tutorial has shown the core of what needs to be done.
Complementing Volley with Picasso
Although Volley provides some great helpers for downloading, caching and displaying images from the network, it can be a little low-level sometimes. I've recently been using Square's Picasso library to handle the image downloading side of things.
Not only does Picasso provide a very nice API, it handles disk and memory based caching without any extra configuration or effort from you - and provides some nice built-in transition effects.
Picasso is a topic for another post, but you can find out more at http://square.github.io/picasso/.
Next Steps
I hope you've found this tutorial helpful for learning about Android's powerful new networking framework. Using Volley to handle your network requests removes vast swathes of boiler-plate code and edge-case tests which means you can concentrate on developing your app's valuable features!
👋 Thanks for reading - I hope you enjoyed this post. If you find it helpful and want to support further writing and tutorials like this one, please consider supporting my work with a coffee!
Support ☕️