Volley
Before Retrofit became the default choice, Google published its own HTTP library: Volley. It is older and less actively developed today, but it is still found in plenty of existing Android codebases, so it is worth knowing how to read and maintain, even if you would not necessarily pick it for a brand new project.
Volley was designed for many small, frequent requests – it was historically popular for simple JSON calls because of its built-in request queuing, prioritization, and response caching, all handled for you without extra setup.
Adding the Dependency
dependencies {
implementation 'com.android.volley:volley:1.2.1'
}
The RequestQueue
Every Volley request is submitted to a RequestQueue – a single central object that manages all outgoing requests for your app, including their priority order, retry policy, and cache. Create one instance and reuse it (a singleton is the common pattern):
// VolleySingleton.java
public class VolleySingleton {
private static VolleySingleton instance;
private final RequestQueue requestQueue;
private VolleySingleton(Context context) {
requestQueue = Volley.newRequestQueue(context.getApplicationContext());
}
public static synchronized VolleySingleton getInstance(Context context) {
if (instance == null) {
instance = new VolleySingleton(context);
}
return instance;
}
public RequestQueue getRequestQueue() {
return requestQueue;
}
}
Fetching Recipes with JsonArrayRequest
Unlike Retrofit, which defines endpoints as a separate interface, Volley requests are built directly where you use them, with success and error listeners passed straight into the constructor:
// Inside RecipeListActivity.java
String url = "https://api.recipebox.example.com/recipes";
JsonArrayRequest request = new JsonArrayRequest(
Request.Method.GET,
url,
null,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
List<Recipe> recipes = parseRecipes(response);
adapter.setRecipes(recipes);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
showError("Could not load recipes");
}
}
);
VolleySingleton.getInstance(this).getRequestQueue().add(request);
- The success (Response.Listener) and error (Response.ErrorListener) callbacks are passed directly as constructor arguments – there is no separate interface to define first, unlike Retrofit's RecipeApiService.
- Volley delivers both listeners on the main thread automatically, so UI updates here need no manual runOnUiThread() call.
- requestQueue.add(request) is what actually schedules and executes the request.
- A JsonObjectRequest works identically but for a single JSON object instead of an array – useful for fetching one recipe by id.
A Custom POST with StringRequest
When you need full control over the request body – for example, posting a new recipe as JSON – a StringRequest lets you override getBody() and getBodyContentType() directly:
String url = "https://api.recipebox.example.com/recipes";
String json = new Gson().toJson(newRecipe);
StringRequest request = new StringRequest(
Request.Method.POST,
url,
response -> onRecipeCreated(),
error -> showError("Could not save recipe")
) {
@Override
public byte[] getBody() {
return json.getBytes(StandardCharsets.UTF_8);
}
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
};
VolleySingleton.getInstance(this).getRequestQueue().add(request);
Volley vs. Retrofit – An Honest Comparison
| Volley | Retrofit | |
|---|---|---|
| Setup for a simple JSON call | Very quick – build a request, add to queue | Requires defining an interface and building a Retrofit instance first |
| Type safety | Weak – you work with raw JSONObject/JSONArray unless you add your own parsing layer | Strong – responses are typed Java objects via Gson |
| Caching & request priority | Built in, no extra configuration | Possible, but needs to be added via OkHttp cache configuration |
| Active development | Maintained, but sees far less active development and community activity today | Actively maintained, the de facto standard for new Java/Kotlin Android projects |
| Scaling to many endpoints | Gets repetitive – each request rebuilds listeners inline | Scales cleanly – add one method per endpoint to the interface |