Volley - http request in blocking way
Asked Answered
V

6

21

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

Vowelize answered 12/7, 2013 at 6:28 Comment(4)
you need to simply start your other calls after you have received the login responseGlazier
(that's what he says in the presentation, anyway)Glazier
There is more complete answer and discussion at #16905241Crassus
Possible duplicate of Can I do a synchronous request with volley?Damascene
C
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) {
}
Chrysolite answered 30/8, 2013 at 21:36 Comment(8)
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?Dangerous
It looks like ExecutionException is the throwable for Volley.Response.ErrorListener.onErrorResponse(). So no need to manually check.Dangerous
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.Chrysolite
As @John Cummings mentioned, you should definitely also read the discussion at #16905241Nonna
InterruptedException should be handled by waiting on future.get() until a response is received. I'll edit answer for clarity.Dehumidify
@Chrysolite how we can pull the volley networkResponse/status code from the ExecutionException object?Mell
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?Celluloid
@Celluloid : future.get() needs to be called from a background thread.Virtually
D
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));
}
Dehumidify answered 2/5, 2015 at 1:23 Comment(0)
A
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.

Aruwimi answered 14/7, 2013 at 3:10 Comment(2)
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.Aruwimi
This was probably downvoted because 1) it doesn't answer the question and 2) the API allows it, using futures.Catechism
P
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);
}
Peggypegma answered 15/12, 2014 at 1:17 Comment(0)
H
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."

Hardhearted answered 17/12, 2016 at 6:26 Comment(0)
A
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();
Abode answered 18/6, 2022 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.