Volley - http request in blocking way

2019-01-17 16:50发布

问题:

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

回答1:

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) {
}


回答2:

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));
}


回答3:

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."



回答4:

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.



回答5:

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);
}