How To Simplify Networking In Android: Introducing The Volley HTTP Library
In a world driven by the Internet, mobile apps need to share and receive information from their products’ back end (for example, from databases) as well as from third-party sources such as Facebook and Twitter. These interactions are often made through RESTful APIs. When the number of requests increases, the way these requests are made becomes very critical to development, because the manner in which you fetch data can really affect the user experience of an app.
In this article, I’d like to take you through my experience of using networking libraries in Android, focusing on APIs. I’ll start with the basics of synchronous and asynchronous programming and cover the complexities of Android threads. We’ll then dive into the AsyncTask module, understand its architectural flows and look at code examples to learn the implementation. I’ll also cover the limitations of the AsyncTask library and introduce Android Volley as a better approach to making asynchronous network calls. We will then delve deeper into Volley’s architecture and cover its valuable features with code examples.
Still interested? Conquering Android networking will take you far in your journey toward becoming a skillful app developer.
Note: A few more Android libraries with networking capabilities are not covered in this article, including Retrofit, OkHttp. I recommend that you go through them to get a glimpse of those libraries.
Programming Approaches Simplified: Sync And Async
“Hold on, Mom, I’m coming,” said Jason, still on his couch, waiting for a text from his girlfriend, who he had texted an hour back. “You could clean your room while you wait for a reply from your friend,” replied Jason’s mother with a hint of sarcasm. Isn’t her suggestion an obvious one? Similar is the case with synchronous and asynchronous HTTP requests. Let’s look at them.
Synchronous requests behave like Jason, staying idle until there is a response from the server. Synchronous requests block the interface, increase computation time and make a mobile app unresponsive. (Not always, though — sometimes it doesn’t make sense to go ahead, such as with banking transactions.) A smarter way to handle requests is suggested by Jason’s mother. In the asynchronous world, when the client makes a request to the server, the server dispatches the request to an event handler, registers for a callback and moves on to the next request. When the response is available, the client is responded to with the results. This is a far better approach, because asynchronous requests let you execute tasks independently.
The diagram above shows how both programming approaches differ from each other in a client-server model. In Android, the UI thread, often known as the main thread, is based on the same philosophy as asynchronous programming.
Android Threads
What Are They And How Do They Work?
Threads are sets of instructions that are managed by the operating system. Multiple threads run under a single process (a Linux process in the case of Android) and share resources such as memory. In Android, when the app runs, the system creates a thread of execution for the whole application, called the “main” thread (or UI thread). The main thread works on a single-threaded model. It is in charge of dispatching events to UI widgets (drawing events), interacting with components from the UI toolkit, such as View.OnClickListener()
, and responding to system events, such as onKeyLongPress()
.
The UI thread runs on an infinite loop and monitors the message queue to check whether the UI needs to be updated. Let’s consider an example. When the user touches a button, the UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts a request to the message queue. The UI thread dequeues the request from the message queue and notifies the widget to take action — in this case, to redraw itself to indicate the button has been pressed. If you’re interested in delving deeper into the internals of the UI thread, you should read about Looper, the MessageQueue and the Handler classes, which accomplish the tasks discussed in our example. As you’d imagine, the UI thread has a lot of responsibilities, such as:
- starting an activity,
- executing a service,
- responding to system callbacks.
Why You Shouldn’t Block The UI Thread
When you think about it, your single-threaded UI thread performs all of its work in response to user interactions. Because everything happens on the UI thread, time-consuming operations such as database queries and network calls will block the UI. The UI thread will dispatch events to the UI widget. The app will perform poorly, and the user will feel that the app is unresponsive. If these tasks take time and the UI thread is blocked for 4 to 5 seconds, Android will throw an “Application Not Responding” (ANR) error. Referring to such an Android app as being not user-friendly would be an understatement, not to mention the poor ratings and uninstalls.
Using the main thread for long tasks would hold things up. Your app will always remain responsive to user events if your UI thread is non-blocking. That is why, if your application requires making network calls, the calls need to be performed on the worker threads that run in the background, not on the main thread. You could use a Java HTTP client library to send and receive data over the network, but the network call itself should be performed by a worker thread. But wait, there’s another issue with Android: thread safety.
Thread Safety And Why It’s So Important!
The Android UI toolkit is not thread-safe. If the worker thread (which performs the task of making network calls) updates the Android UI toolkit, it could result in undefined and unexpected behavior. This can be difficult and time-consuming to track down. The single-thread model ensures that the UI is not modified by different threads at the same time. So, if we have to update the ImageView with an image from the network, the worker thread will perform the network operation in a separate thread, while the ImageView will be updated by the UI thread. This makes sure that the operations are thread-safe, with the UI thread providing the necessary synchronization. It also helps that the UI thread is always non-blocking, because the actual task happens in the background by the worker thread.
In summary, follow two simple rules in Android development:
- The UI thread should not be blocked.
- The UI toolkit should not be directly updated from a non-UI worker thread.
A Note On Android Services
When you talk about making requests from an “activity,” you will come across Android “services.” A service is an app component that can perform long operations in the background without the app being active or even when the user has switched to another app. For example, playing music or downloading content in the background can be done well with services. If you choose to work with a service, it will still run in your application’s main thread by default, so you’ll need to create a new thread within the service to handle blocking operations. If you need to perform work outside of your main thread while the user is interacting with your app, you are better off using a networking library such as AsyncTask or Volley.
Performing tasks in worker threads is great, but as your app starts to perform complex network operations, worker threads can get difficult to maintain.
Down The Rabbit Hole With AsyncTask
It’s quite clear now that we should use a robust HTTP client library and ensure that the network task is achieved in the background using worker threads — essentially, with non-UI threads.
Android does have a resource to help handle network calls asynchronously. AsyncTask is a module that allows us to perform asynchronous work on the user interface.
What Does AsyncTask Give Us?
AsyncTask performs all of the blocking operations in a worker thread, such as network calls, and publishes the results once it’s done. The UI thread gets these results and updates the user interface accordingly.
Here is how I implemented an asynchronous worker thread using AsyncTask:
- Subclass AsyncTask to implement the
onPreExecute()
method, which will create a toast message suggesting that the network call is about to happen. - Implement the
doInBackground(Params...)
method. As the name suggests,doInBackground
is the worker thread that makes network calls and keeps the main thread free. - Because the worker thread cannot update the UI directly, I implemented my own
postExecute(Result)
method, which will deliver the results from the network call and run in the UI thread so that the user interface can be safely modified. - The progress of the background task can be published from the worker thread with the
publishProgress()
method and can be updated on the UI thread using theonProgressUpdate(Progress...)
method. These methods are not implemented in the example code but are fairly straightforward to work with. - Finally, call the asynchronous task using the
execute()
method from the UI thread.
Note: execute()
and postExecute()
both run on the UI thread, whereas doInBackground()
is a non-UI worker thread.
In the context of my app, I make a POST
request on a REST API to start a calling session for a campaign. I also pass the access token in the request header and the campaign ID in the body. If you look at the code, java.net.HttpURLConnection
is used to make a network call, but the actual work is done in the doInBackground()
method of AsyncTask. In the example above, we also make use of the application context to pop up toast messages, but AsyncTasks can be defined as inner classes in activities if they are small enough, avoiding the need for the Context
property.
Generic Types In AsyncTask
A generic type is a generic class or interface that is parameterized over types. Just like how we define formal parameters used in method declarations, type parameters help you to reuse the same code with different input types. While inputs to methods are values, inputs to type parameters are types. There are three types used by an asynchronous task:
Params
The type of the parameters sent to the task upon execution.Progress
The type of the progress units published during the background computation.Result
The type of the result of the background computation.
This is how I have extended AsyncTask with types:
public class MyAsync extends AsyncTask<String, Void, Integer>
So, the Params
sent to the task are of type String
; Progress
is set to Void
; and the Result
is of type Integer
. In our implementation, we’re passing the URL (type String
) to the doInBackground(String… params)
method; while we don’t set a Progress
type, we pass the status code of the response (type Integer
) to onPostExecute(Integer integer)
. Not all types are used by an asynchronous task; and to mark a type as unused, we use type Void
.
The code is available for downloading on GitHub:
Struggles With AsyncTask
Working with AsyncTask is pretty nice until you start doing more complex operations with it. A few instances where AsyncTask would not be useful are highlighted below:
- If a background task is running using AsyncTask and the user rotates the screen, the entire activity will be destroyed and recreated. As a result, the reference to the activity will be lost, and the result of AsyncTask will update the UI elements that don’t exist anymore. By the way, if we have to handle this in AsyncTask, we have to monitor for activity getting destroyed in the
onPostExecute
method. - Cancelling requests with AsyncTask ensures that the
postExecute()
method is not called. Unfortunately, it doesn’t actually make the request every time. This behavior is not implicit, and it’s the job of the developer to explicitly cancel asynchronous tasks. - AsyncTask does not provide the facility of caching results, which can be a setback. Often, an image such as a thumbnail is displayed several times. To reduce bandwidth when displaying this image, we can use the help of caching mechanisms.
- There are limits on how many parallel tasks you can run with AsyncTask. It can handle 128 concurrent tasks, with a queue length of 10. So, be aware when you’re crossing those limits. These limits are derived from ThreadPoolExecutor.
Even though AsyncTask does a good job of performing asynchronous operations, its utility can be limiting due to the reasons mentioned above. Luckily, we have Volley at our disposal, an Android module for making asynchronous network calls.
Enter Volley
Volley is a networking library developed by Google and introduced at Google I/O 2013. In Volley, all network calls are asynchronous by default, so you don’t have to worry about performing tasks in the background anymore. Volley considerably simplifies networking with its cool set of features.
Volley’s Architecture
Before looking at the code, let’s get ourselves elbow-deep in Volley and understand its architecture. Below is a high-level architectural diagram of Volley’s flow. It works in a very simple way:
- Volley runs one cache processing thread and a pool of network dispatch threads.
- Network call requests are first triaged by the cache thread. If the response can be served from the cache, then the cached response is parsed by
CacheDispatcher
and delivered back to the main thread, the UI thread. - If the result is not available in the cache, then a network request needs to be made to get the required data, for which the request is placed in the network queue.
- The first available network thread (
NetworkDispatcher
) takes the request from the queue. It then performs the HTTP request, parses the response on the worker thread and writes the response to cache. It then delivers the parsed response back to the main thread.
If you carefully analyze Volley’s architecture, you’ll see that it solves issues that we face with AsyncTask:
- With Volley, we don’t have to worry about getting work done in the background (
doInBackground()
from AsyncTask) because the library makes asynchronous network calls and manages it for you inNetworkDispatcher
. - Updating the results back to the UI thread from the worker thread is handled by Volley, even without your noticing it.
- Volley frees you from having to write boilerplate code and allows you to concentrate on the tasks specific to your app.
- Volley has nice set of features, too. To name a few:
- Volley caches API responses, so if you make the same request twice, the results are fetched from the cache. This is really useful, as in the case of loading the same image multiple times — we can reduce bandwidth usage by getting the image from cache. We’ll address how to implement the cache in Volley soon.
- Volley helps you to prioritize requests. If there are multiple network calls, you could prioritize a given network call based on its impact and importance.
- You can easily cancel or retry requests. You have the flexibility to cancel a single request or to cancel blocks of requests.
- Volley has strong ordering, which makes it easy to populate the UI in sequence while still fetching data asynchronously.
- Volley can handle multiple request types, such as string and JSON. In fact, Volley is perfect for API calls such as JSON objects and lists, and it makes working with RESTful applications very easy.
- Image loading is one of the more useful features of Volley. You can write a custom ImageLoader and complement it with the LRU bitmap cache to make network calls for images. We’ll talk about that a bit later.
- Volley is maintained by Google, which takes care of fixing bugs. That definitely doesn’t hurt, does it!
Let’s see how to make asynchronous calls using Volley. Start by including Volley in your Android project.
Add Volley To Your Project Easily
The easiest way to add Volley to your project is to add the following dependency to your app’s build.gradle
file.
app/build.gradle
dependencies {
compile 'com.android.volley:volley:x.y.z'
}
Another way to do this is by cloning the Volley repository. Build Volley with Ant, copy the built volley.jar
file in the libs
folder, and then create an entry in build.gradle
to use the jar
file. Here’s how:
git clone https://android.googlesource.com/platform/frameworks/volley
cd volley
android update project -p .
ant jar
You can find the generated volley.jar
in Volley’s bin folder. Copy it to your libs
folder in Android Studio, and add the entry below to app/build.gradle
:
app/build.gradle
dependencies {
compile files('libs/volley.jar')
}
And you’re done! You have added Volley to your project without any hassle. To use Volley, you must add the android.permission.INTERNET
permission to your app’s manifest. Without this, your app won’t be able to connect to the network.
manifests/AndroidManifest.xml
<uses-feature android:name="android.hardware.wifi" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
“Hello World” With Volley: Handling Standard String Requests
The code example below shows you how to make a request on https://api.ipify.org/?format=json
, get the response and update the text view of your app. We use Volley by creating a RequestQueue
and passing it Request
objects. The RequestQueue
manages the worker threads and makes the network calls in the background. It also takes care of writing to cache and parsing the response. Volley takes the parsed response and delivers it to the main thread. Appropriate code constructs are highlighted with comments in the code snippet below. I haven’t implemented caching yet; I’ll talk about that in the next example.
MainActivity.java
package com.example.chetan.androidnetworking;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Set the title of Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Get the ID of button that will perform the network call
Button btn = (Button) findViewById(R.id.button);
assert btn != null;
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = "https://api.ipify.org/?format=json";
final TextView txtView = (TextView) findViewById(R.id.textView3);
assert txtView != null;
//Request a string response from the URL resource
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener() {
@Override
public void onResponse(String response) {
// Display the response string.
txtView.setText("Response is: " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
txtView.setText("Oops! That didn’t work!");
}
});
//Instantiate the RequestQueue and add the request to the queue
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
queue.add(stringRequest);
}
});
}
Caching Responses With Volley
To set up the cache, we have to implement a disk-based cache and add the cache object to the RequestQueue
. I set up a HttpURLConnection to make the network requests. Volley’s toolbox provides a standard cache implementation via the DiskBasedCache
class, which caches the data directly on the hard disk. So, when the button is clicked for the first time, a network call is made, but in the next occurence of a button click, I get the data from the cache. Nice!
MainActivity.java
package com.example.chetan.androidnetworking;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Set the title of Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Get the ID of button, which will perform the network call
Button btn = (Button) findViewById(R.id.button);
assert btn != null;
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = "https://api.ipify.org/?format=json";
final TextView txtView = (TextView) findViewById(R.id.textView3);
assert txtView != null;
// Setup 1 MB disk-based cache for Volley
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024);
// Use HttpURLConnection as the HTTP client
Network network = new BasicNetwork(new HurlStack());
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener() {
@Override
public void onResponse(String response) {
// Display the string response on the UI
txtView.setText("Response is: " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
txtView.setText("Oops! That didn’t work!");
}
});
// Instantiate the RequestQueue with the cache and network, start the request
// and add it to the queue
RequestQueue queue = new RequestQueue(cache, network);
queue.start();
queue.add(stringRequest);
}
});
}
Setting Up Singleton Class For RequestQueue
If you have to fire network requests in multiple Android activities, you should avoid using Volley.newRequestQueue.add()
, as we did in the first example. You can develop a singleton class for the RequestQueue
and use it across your project. Creating a RequestQueue
as a singleton is recommended, so that the RequestQueue
lasts for the lifetime of your app. It also ensures that the same RequestQueue
is utilized even when the activity is recreated, as in case of a screen rotation.
VolleyController.java
package com.example.chetan.androidnetworking;
import android.content.Context;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
public class VolleyController {
private static VolleyController mInstance;
private RequestQueue mRequestQueue;
private static Context mCtx;
private VolleyController(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
}
public static synchronized VolleyController getInstance(Context context) {
// If instance is not available, create it. If available, reuse and return the object.
if (mInstance == null) {
mInstance = new VolleyController(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key. It should not be activity context,
// or else RequestQueue won’t last for the lifetime of your app
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public void addToRequestQueue(Request req) {
getRequestQueue().add(req);
}
}
You can now use the VolleyController
in your MainActivity
like this: VolleyController.getInstance(getApplicationContext()).addToRequestQueue(stringRequest);
. Or you can create a queue in this way: RequestQueue queue = VolleyController.getInstance(this.getApplicationContext()).getRequestQueue();
. Note the use of ApplicationContext
in these examples.
Volley: Working With JSON Requests
In Volley, you can set up up a custom JSON request by extending the Request
class. This will help you to parse and deliver network responses. You can also do more things such as set request priorities and set up cookies with this custom class. Below is the code for creating a custom JSONObject Request
in Volley. You can handle ImageRequest
types in the same manner.
CustomJSONObjectRequest.java
package com.example.chetan.androidnetworking;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.util.Map;
public class CustomJSONObjectRequest extends Request {
private Listener listener;
private Map<String, String> params;
Priority mPriority;
public CustomJSONObjectRequest(int method, String url, Map<String, String> params,
Listener responseListener, ErrorListener errorListener) {
super(method, url, errorListener);
this.listener = responseListener;
this.params = params;
}
protected Map<String, String> getParams()
throws com.android.volley.AuthFailureError {
return params;
};
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
@Override
protected void deliverResponse(JSONObject response) {
listener.onResponse(response);
}
}
With asynchronous tasks, you can’t know when the response will arrive from your API. You need to execute a Volley request and wait for the response in order to parse and return it. You can do this with the help of a callback. Callbacks can be easily implemented with Java interfaces. The code below shows how to build your callback with the help of the VolleyCallback
interface.
VolleyCallback.java
package com.example.chetan.androidnetworking;
import org.json.JSONException;
import org.json.JSONObject;
public interface VolleyCallback {
void onSuccess(JSONObject result) throws JSONException;
void onError(String result) throws Exception;
}
Now, let’s make a network call using the custom JSON request class and update the UI with the response.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button btn = (Button) findViewById(R.id.button);
assert btn != null;
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = "https://api.ipify.org/?format=json";
final TextView txtView = (TextView) findViewById(R.id.textView3);
assert txtView != null;
makeRequest(url, new VolleyCallback() {
@Override
public void onSuccess(JSONObject result) throws JSONException {
Toast.makeText(getApplicationContext(), "Hurray!!",
Toast.LENGTH_LONG).show();
txtView.setText(String.format("My IP is: %s", result.getString("ip")));
txtView.setTextColor(Color.BLUE);
}
@Override
public void onError(String result) throws Exception {}
});
}
});
}
// Custom JSON Request Handler
public void makeRequest( final String url, final VolleyCallback callback) {
CustomJSONObjectRequest rq = new CustomJSONObjectRequest(Request.Method.GET,
url, null,
new Response.Listener() {
//Pass response to success callback
@Override
public void onResponse(JSONObject response) {
Log.v("Response", response.toString());
try {
String ip = response.getString("ip");
if (ip != "null") {
callback.onSuccess(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
return headers;
}
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
return params;
}
};
// Request added to the RequestQueue
VolleyController.getInstance(getApplicationContext()).addToRequestQueue(rq);
}
Request An Image With Volley
Volley offers the following classes for requesting images:
ImageRequest
This is used to get an image at the given URL. It also helps with resizing images to the size you need, and all of this happens in the worker thread.ImageLoader
This class handles the loading and caching of images from remote URLs.NetworkImageView
This replacesImageView
when the image is being fetched from a URL via the network call. It also cancels pending requests if theImageView
detaches and is no longer available.
For caching images, you should use the in-memory LruBitmapCache
class, which extends LruCache. LRU stands for “least recently used”; this type of caching makes sure that the least used objects are removed first from the cache when it gets full. So, when loading a bitmap into an ImageView
, the LruCache
is checked first. If an entry is found, it is used immediately to update the ImageView
; otherwise, a background thread is spawned to process the image. Just what we want!
Retry Mechanism In Volley
Volley does retry network calls if you have set the retry policy for your requests. We can change the retry values for each request using setRetryPolicy()
. This is implemented in the DefaultRetryPolicy
class of Android. You can set the retry policy for a request in this manner:
rq.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
DEFAULT_TIMEOUT_MS
is the default socket timeout in milliseconds. DEFAULT_MAX_RETRIES
is the maximum number of request retries you want to perform. And DEFAULT_BACKOFF_MULT
is the default backoff multiplier, which determines the exponential time set to the socket for every retry attempt.
Volley: Error-Handling
Volley can catch network errors very easily, and you don’t have to bother much with cases in which there is a loss of network connectivity. In my app, I’ve chosen to handle network errors with the error message “No Internet access.”
The code below shows how to handle NoConnection
errors.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button btn = (Button) findViewById(R.id.button);
assert btn != null;
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = "https://api.ipify.org/?format=json";
final TextView txtView = (TextView) findViewById(R.id.textView3);
assert txtView != null;
makeRequest(url, new VolleyCallback() {
@Override
public void onSuccess(JSONObject result) throws JSONException {
Toast.makeText(getApplicationContext(), "Hurray!!",
Toast.LENGTH_LONG).show();
txtView.setText(String.format("My IP is: %s", result.getString("ip")));
txtView.setTextColor(Color.BLUE);
}
@Override
public void onError(String result) throws Exception {
Toast.makeText(getApplicationContext(), "Oops!!",
Toast.LENGTH_LONG).show();
txtView.setText(result);
txtView.setTextColor(Color.RED);
}
});
}
});
}
public void makeRequest( final String url, final VolleyCallback callback) {
CustomJSONObjectRequest rq = new CustomJSONObjectRequest(Request.Method.GET,
url, null,
new Response.Listener() {
@Override
public void onResponse(JSONObject response) {
Log.v("Response", response.toString());
try {
String ip = response.getString("ip");
if (ip != "null") {
callback.onSuccess(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.v("Response", error.toString());
String err = null;
if (error instanceof com.android.volley.NoConnectionError){
err = "No internet Access!";
}
try {
if(err != "null") {
callback.onError(err);
}
else {
callback.onError(error.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
return headers;
}
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
return params;
}
};
rq.setPriority(Request.Priority.HIGH);
VolleyController.getInstance(getApplicationContext()).addToRequestQueue(rq);
}
}
Volley: Adding Request Headers
To make API calls to third-party REST APIs, you need to pass API access tokens or have support for different authorization types. Volley lets you do that easily. Add the headers to the HTTP GET call using the headers.put(key,value)
method call:
MainActivity.java
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
return headers;
}
Volley: Prioritizing Requests
Setting priorities for your network calls is required in order to differentiate between critical operations, such as fetching the status of a resource and pulling its meta data. You don’t want to compromise a critical operation, which is why you should implement priorities. Below is an example that demonstrates how you can use Volley to set priorities. Here, we are using the CustomJSONObjectRequest
class, which we defined earlier, to implement the setPriority()
and getPriority()
methods, and then in the MainActivity
class, we are setting the appropriate priority for our request. As a rule of thumb, you can use these priorities for the relevant operations:
Priority.LOW
// images, thumbnailsPriority.NORMAL
// standard queriesPriority.HIGH
// descriptions, listsPriority.IMMEDIATE
// login, logout
CustomJSONObjectResponse.java
public void setPriority(Priority priority) {
mPriority = priority;
}
@Override
public Priority getPriority() {
// Priority is set to NORMAL by default
return mPriority != null ? mPriority : Priority.NORMAL;
}
MainActivity.java
// set priority to HIGH
rq.setPriority(Request.Priority.HIGH);
Advantages Of Volley
Volley is a useful library and can save the day for a developer. It’s an integral part of my toolkit, and it would be a huge win for a development team in any project. Let’s review Volley’s benefits:
- It is responsive to user interactions, because all network processes happen asynchronously. The UI thread always remain free to handle any user interaction.
- It handles networking tasks asynchronously. Whenever a network request is made, a background thread is created to process network calls.
- Volley improves the lag time between requests because it can make multiple requests without the overhead of thread management.
- Google has made considerable efforts to improve the performance of the Volley library by improving memory usage patterns and by passing callbacks to the main thread in a batch. This reduces context switching and will make your app faster.
Summary
- The UI thread, or the main thread, in Android does the work of dispatching events to the UI toolkit and is responsible for dequeueing the request from message queue to notify the widget to take action. That’s why it’s important that the UI thread always be non-blocking.
- Android has its own HTTP client libraries, such as
HttpURLConnection
, which help you to perform synchronous network calls. To keep the main thread non-blocking, the network calls need to be performed in worker threads that run in the background. - Android’s AsyncTask library helps you run tasks in the background and ensures that your main thread is non-blocking. It also ensures that a background task doesn’t directly update the UI. Instead, it returns the result to the UI thread.
- AsyncTask has its limitations, such as not being able to cache responses and not being able to handle parallel requests. It also doesn’t gracefully handle scenarios such as screen rotation when background tasks are running.
- Making asynchronous network calls using Volley is a cleaner solution to developing Android apps. Volley has an awesome set of features, such as caching, request cancellation and prioritization.
- Volley can handle multiple request types, such as JSON, images and text, and it performs better than AsyncTask.
I hope you’ve enjoyed the article. All of the code examples are available for downloading. The complete app is hosted on GitHub.
Further Reading
- What Every App Developer Should Know About Android
- Mobile Design Practices For Android: Tips And Techniques
- Get Started Developing For Android With Eclipse
- How To Design For Android