One of the newest frameworks in Android is Volley. This library was introduced to ease the process of making a REST web service call simpler. Usually the android volley library is used to make a request in background. For example, when a web service is called from an activity through volley library. A background worker thread is spawned, and in that thread web API call is made. After which the result is captured and returned back to the activity i.e the main UI thread. But in this tutorial I would show you how to make an Android volley synchronous request. That is, the calling thread would wait until the result is returned from the worker thread.
A point to note here is that, in any situation we should not do any sort of heavy work in the main thread. Also if you try to perform the below mentioned approach on the main thread of your app, you would get a TimeoutException. Therefore for making an android volley synchronous request I would spawn a thread and will make that thread wait until the response is captured.
Android Volley library is available through open AOSP repository. But as mentioned in this tutorial, there are two ways to include and use Volley.
Gradle Dependency
Include a gradle dependency in your project’s build.gradle (app) file:
compile 'com.android.volley:volley:1.0.0'
or
Library Project
Steps to include the Android Volley library in your project are:
- Clone the repo from this url:
git clone https://android.googlesource.com/platform/frameworks/volley
It can be done through any of the leading tools like Atlassian SourceTree, TortoiseGIT or through the GIT command line.
- In Android Studio goto File -> Import Module -> Specify the directory
- Enter the module name- “:Volley” (for internal project reference), hit finish.
- By now the gradle would start building the project. Let it complete.
- Open build.gradle file for your app module
- In dependencies block add a line
compile project(':Volley')
- Go to Build menu -> Rebuild Project
- You are done importing Android Volley to you project.
Before starting with the code don’t forget to add an internet permission in your android manifest:
<uses-permission android:name="android.permission.INTERNET"/>
Custom Volley RequestQueue
To understand the basic working of volley, please have a look at my Android Volley tutorial. When using volley I prefer to use a single RequestQueue for my application, as it is easy to keep track of requests and responses throughout the app. Hence in this Android Volley | Making A Synchronous Request tutorial as-well I have customized the volley RequestQueue, by making it a singleton class. This way only one instance of volley RequestQueue would exist. Have a look at the code below:
package com.truiton.volleyblockingrequest; import android.content.Context; import com.android.volley.Cache; import com.android.volley.Network; import com.android.volley.RequestQueue; import com.android.volley.toolbox.BasicNetwork; import com.android.volley.toolbox.DiskBasedCache; import com.android.volley.toolbox.HurlStack; public class CustomVolleyRequestQueue { private static CustomVolleyRequestQueue mInstance; private static Context mCtx; private RequestQueue mRequestQueue; private CustomVolleyRequestQueue(Context context) { mCtx = context; mRequestQueue = getRequestQueue(); } public static synchronized CustomVolleyRequestQueue getInstance(Context context) { if (mInstance == null) { mInstance = new CustomVolleyRequestQueue(context); } return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { Cache cache = new DiskBasedCache(mCtx.getCacheDir(), 10 * 1024 * 1024); Network network = new BasicNetwork(new HurlStack()); mRequestQueue = new RequestQueue(cache, network); // Don't forget to start the volley request queue mRequestQueue.start(); } return mRequestQueue; } }
Although to make an Android Volley synchronous request, it is not necessary to customize the RequestQueue
. As I mentioned above its just a good way of doing it. In the above class if you see closely, you would notice that cache limit for the request queue has also been increased. In standard vanilla implementation of volley, the limit of DiskBasedCache
is of 5 MB, for this example I have raised to 10 MB. This would allow us to cache a larger amount of requests. You can define it accordingly, as per your requirement.
Can I do a synchronous request with volley?
The answer is YES, though Android Volley we can perform a synchronous or blocking request. But first lets define a layout where all this action would happen.
<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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".VolleyBlockingRequestActivity" android:background="#FFFFFF"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageView" android:src="@drawable/truiton_short" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Request" android:id="@+id/button" android:layout_below="@+id/imageView" android:layout_alignLeft="@+id/imageView" android:layout_alignStart="@+id/imageView" android:layout_alignRight="@+id/imageView" android:layout_alignEnd="@+id/imageView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/textView" android:layout_below="@+id/button" android:layout_marginTop="26dp" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout>
To make an actual Android Volley blocking request lets define the activity:
package com.truiton.volleyblockingrequest; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; 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.toolbox.JsonObjectRequest; import com.android.volley.toolbox.RequestFuture; import org.json.JSONException; import org.json.JSONObject; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class VolleyBlockingRequestActivity extends AppCompatActivity { public static final String REQUEST_TAG = "VolleyBlockingRequestActivity"; private TextView mTextView; private Button mButton; private RequestQueue mQueue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_volley_blocking_request); mTextView = (TextView) findViewById(R.id.textView); mButton = (Button) findViewById(R.id.button); } @Override protected void onStart() { super.onStart(); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startParsingTask(); } }); } @Override protected void onStop() { super.onStop(); if (mQueue != null) { mQueue.cancelAll(REQUEST_TAG); } } public void startParsingTask() { Thread threadA = new Thread() { public void run() { ThreadB threadB = new ThreadB(getApplicationContext()); JSONObject jsonObject = null; try { jsonObject = threadB.execute().get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } final JSONObject receivedJSONObject = jsonObject; runOnUiThread(new Runnable() { @Override public void run() { mTextView.setText("Response is: " + receivedJSONObject); if (receivedJSONObject != null) { try { mTextView.setText(mTextView.getText() + "\n\n" + receivedJSONObject.getString("name")); } catch (JSONException e) { e.printStackTrace(); } } } }); } }; threadA.start(); } private class ThreadB extends AsyncTask<Void, Void, JSONObject> { private Context mContext; public ThreadB(Context ctx) { mContext = ctx; } @Override protected JSONObject doInBackground(Void... params) { final RequestFuture<JSONObject> futureRequest = RequestFuture.newFuture(); mQueue = CustomVolleyRequestQueue.getInstance(mContext.getApplicationContext()) .getRequestQueue(); String url = "http://api.openweathermap.org/data/2.5/weather?q=London,uk"; final JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method .GET, url, new JSONObject(), futureRequest, futureRequest); jsonRequest.setTag(REQUEST_TAG); mQueue.add(jsonRequest); try { return futureRequest.get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } return null; } } }
In the above class I have started a standard java thread, on click of a button. This is the entry point of making a blocking request through volley. Since Android has a guideline that you can’t make network calls on main thread, it throws a network on main thread exception. I had to spawn a new thread, to make an environment where this synchronous type of request would actually come in use.
Please Note : In normal situations you should not be making this type of request.
After spawning a new thread called ThreadA
, I started an AsyncTask (ThreadB
) so that this can be put into waiting state, by calling get(long, TimeUnit)
method of RequestFuture class. A volley class used for blocking requests. To access the get()
method and make an Android Volley synchronous request, you need to use the RequestFuture
class instead of standard StringRequest
or JsonObjectRequest
class. This RequestFuture
class also implements both Response.Listener
and Response.ErrorListener
interfaces. Therefore it can be used as a listener as-well while making a request. The RequestFuture.get(long, TimeUnit)
method returns the result immediately, as internally it calls the doGet(long)
method, in a synchronized way. Hence instead of receiving the result back in onResponse(T response)
method, we are actually waiting till the response is captured.
Now since our AsyncTask (ThreadB
) was also started in a blocking way by calling AsyncTask.get (long timeout, TimeUnit unit) method from ThreadA
. Apparently ThreadA
is also blocked at the moment. This is the key reason this cant be done on the main thread of your app. I tried doing it in the main thread and was not able to do it, as it used to throw a TimeoutException.
After completing the code, the screen should look something like the screenshot below:
To understand the example better, please have a look at the full source code below:
With this I would like to conclude my tutorial on how to make a blocking request with Android volley. If this tutorial helped you share it with your friends, also please like our Facebook, Google+ page.
Born in New Delhi, India. A software engineer by profession, an android enthusiast and an evangelist. My motive here is to create a group of skilled developers, who can develop something new and good. Reason being programming is my passion, and also it feels good to make a device do something you want. In a very short span of time professionally I have worked with many tech firms. As of now too, I am employed as a senior engineer in a leading tech company. In total I may have worked on more than 20 projects professionally, and whenever I get spare time I share my thoughts here at Truiton.
Hi!
There is a problem with the RequestFuture get() method. If a response is delivered, in the onResponse method the notifyAll() method is called. This will cause an InterruptedException in the doGet() method. So the response is delivered but it causes an InterruptedException by request.get(). The only way to solve it is to call request.get() again when an InterruptedException happens.
What if the Volly Request is Running in a Service ?