20

I'm learning how to use Google Volley these days. It's very convenient for fast networking. It seems that all the requests are running in background in Volley. For example:

volleyRequestQueue.add(new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, new SignUpResponseListener(), new MyErrorListener()));

Using the above code, we can make a POST call which runs in background(non-blocking way). Now my question is : Is it possible to make the POST call in the blocking way? Why I need a blocking way to make a REST call? Because some calls, like sign in, should be done before doing something else.

Thanks

georgiecasey
  • 21,793
  • 11
  • 65
  • 74
Fihop
  • 3,127
  • 9
  • 42
  • 65
  • you need to simply start your other calls after you have received the login response – njzk2 Jul 15 '13 at 15:35
  • (that's what he says in the presentation, anyway) – njzk2 Jul 15 '13 at 15:45
  • 2
    There is more complete answer and discussion at http://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley – John Cummings Oct 18 '14 at 18:04
  • Possible duplicate of [Can I do a synchronous request with volley?](https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley) – Bharat Jun 29 '17 at 08:20

6 Answers6

31

Volley supports blocking request via RequestFutures. You create a normal request but set its callbacks as your request future, which is just volley's extension of a standard java futures. The call to future.get() will block.

It looks something like this

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future)
volleyRequestQueue.add(request);

try {
    JSONObject response = future.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
Gabriel
  • 2,279
  • 1
  • 20
  • 17
  • 3
    since we pass the `RequestFuture` to the `JsonObjectRequest` constructor in place of both the success and error listener, does that mean when we `future.get()` we manually have to differentiate between success and error? – tir38 Mar 19 '14 at 16:07
  • It looks like `ExecutionException` is the throwable for `Volley.Response.ErrorListener.onErrorResponse()`. So no need to manually check. – tir38 Mar 19 '14 at 16:15
  • 3
    Exactly, it throws ExecutionException on failures and you can pull the volley networkRequest object from the exception it to check the status code/request body. – Gabriel Apr 01 '14 at 20:12
  • 3
    As @John Cummings mentioned, you should definitely also read the discussion at http://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley – Jedidja Oct 20 '14 at 00:18
  • 2
    `InterruptedException` should be handled by waiting on `future.get()` until a response is received. I'll edit answer for clarity. – Jeff Lockhart May 02 '15 at 00:41
  • 1
    @Gabriel how we can pull the volley networkResponse/status code from the ExecutionException object? – Min2 May 18 '15 at 13:46
  • 1
    I did this, but added a timeout. But I always seem to get a timeout. While adding using a regular listener I always get a response very quickly. Any ideas as to why? – Cruncher Dec 03 '15 at 19:34
  • @Cruncher : `future.get()` needs to be called from a background thread. – Hemanth Aug 19 '16 at 11:52
4

Here's a clearer answer which handles InterruptedException properly as well as timeout. Note that the only time you would want to swallow the interrupt and continue is if you specifically intend to use the interrupt to cancel responding to the request.

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Method.POST, SIGNUP_URL, reqBody, future, future);
volleyRequestQueue.add(request);

try {
    JSONObject response = null;
    while (response == null) {
        try {
            response = future.get(30, TimeUnit.SECONDS); // Block thread, waiting for response, timeout after 30 seconds
        } catch (InterruptedException e) {
            // Received interrupt signal, but still don't have response
            // Restore thread's interrupted status to use higher up on the call stack
            Thread.currentThread().interrupt();
            // Continue waiting for response (unless you specifically intend to use the interrupt to cancel your request)
        }
    }
    // Do something with response, i.e.
    new SignUpResponseListener().onResponse(response);
} catch (ExecutionException e) {
    // Do something with error, i.e.
    new MyErrorListener().onErrorResponse(new VolleyError(e));
} catch (TimeoutException e) {
    // Do something with timeout, i.e.
    new MyErrorListener().onErrorResponse(new VolleyError(e));
}
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Jeff Lockhart
  • 5,379
  • 4
  • 36
  • 51
1

If you want to do something exactly after the Volley request, use a callback listener onSuccess (SignUpResponseListener in that case) and put the code there. This is the best practice.

Makibo
  • 1,679
  • 21
  • 31
  • Please explain the downvote. I am fully aware the user asked for a blocking call, but the reasoning was only "to execute other code afterwards.", so my suggestion is still a valid solution to his requirements. – Makibo Apr 28 '14 at 01:42
  • 3
    This was probably downvoted because 1) it doesn't answer the question and 2) the API allows it, using futures. – spaaarky21 Jan 28 '15 at 22:36
1

I couldn't get RequestFuture working so I just used a callback listener like Makibo suggested. I've no idea why he was downvoted, this is probably the best solution for the original common problem of different Volley requests that all depends on an initial Login or something. In this example, I want to upload a photo but first I've to check if user is logged in or not. If not, I've to login and then wait for success before uploading the photo. If already logged in, just go straight to uploading the photo.

Here's my sample code:

// interface we'll use for listener
public interface OnLoginListener {
    public void onLogin();
}

public void uploadPhoto(final String username, final String password, final String photo_location) {
    // first setup callback listener that will be called if/when user is logged in
    OnLoginListener onLoginListener=new OnLoginListener() {
        @Override
        public void onLogin() {
            uploadPhotoLoggedIn(photo_location);
        }
    };
    // simplistic already logged in check for this example, just checking if username is null
    if (loggedInUsername==null) {
        // if it null, login and pass listener
        httpLogin(username, password, onLoginListener);
    } else {
        // if not null, already logged in so just call listener method
        onLoginListener.onLogin();
    }
}

public void httpLogin(String username, String password, final OnLoginListener onLoginListener) {
    StringRequest loginRequest = new StringRequest(Request.Method.POST, "https://www.example.com/login.php", new Response.Listener<String>() { 
        @Override
        public void onResponse(String txtResponse) {
            Log.d("STACKOVERFLOW",txtResponse);
            // call method of listener after login is successful. so uploadPhotoLoggedIn will be called now
            onLoginListener.onLogin();
        } }, 
        new Response.ErrorListener() 
        {
            @Override
            public void onErrorResponse(VolleyError error) {
                // TODO Auto-generated method stub
                Log.d("VOLLEYERROR","error => "+error.toString());
            }
        }
            ) {
    };    
    // Just getting the Volley request queue from my application class (GetApplicatio.java), and adding request
    GetApplication.getRequestQueue().add(loginRequest);
}
georgiecasey
  • 21,793
  • 11
  • 65
  • 74
1

I want to add something to Gabriel's answer. While RequestFuture blocks the thread from which it is called and it serves your purpose, the network request itself is not carried out in that thread. Instead, it is carried out on a background thread.

From what I understand after going through the library, requests in the RequestQueue are dispatched in its start() method:

    public void start() {
        ....
        mCacheDispatcher = new CacheDispatcher(...);
        mCacheDispatcher.start();
        ....
           NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
           networkDispatcher.start();
        ....
    }

Now both CacheDispatcher and NetworkDispatcher classes extend thread. So effectively a new worker thread is spawned for dequeuing the request queue and the response is returned to the success and error listeners implemented internally by RequestFuture.

So I see no point in making a separate blocking thread to use RequestFuture. Instead as Makibo mentioned in his answer - " use a callback listener onSuccess (SignUpResponseListener in that case) and put the code there."

Vignatus
  • 43
  • 8
0

For me without the timeout in get() it always blocked, and with a timeout it always timed out.

Turns out it not must be on the main UI thread, therefore create a thread to make the async requests:

Thread t = new Thread(() -> {
    doAsyncRequestHere();
});
t.start();
trex
  • 325
  • 3
  • 8