5

Implementing my LoginActivity.java, I setup another file+class called AuthClient which uses Volley heavily. UserLoginTask needs to return a Boolean from its doInBackground so that the remote call to login succeeds/fails.

So following Can I do a synchronous request with volley?, I setup:

void login(final HashMap<String, String> data,
           Response.Listener<JSONObject> listener,
           Response.ErrorListener errorListener) {
    JsonObjectRequest req = new JsonObjectRequest(
            api_prefix + "/auth", new JSONObject(data), listener, errorListener
    );
    queue.add(req); // RequestQueue
}

boolean loginResponseHandler(RequestFuture<VolleyError> err_future,
                             RequestFuture<JSONObject> future) {
    try {
        VolleyError err_response = err_future.get();
        JSONObject response = future.get();
        System.out.println("response.toString() = " + response.toString());
        return true;
    } catch (InterruptedException | ExecutionException e) {
        System.err.println("e = " + e.toString());
        return false;
    } catch (Exception e) { // Even tried this!
        System.err.println("Exception::e = " + e.toString());
        throw e;
    }
}

@Override
protected Boolean doInBackground(Void... params) {
    RequestFuture<JSONObject> future = RequestFuture.newFuture();
    RequestFuture<VolleyError> err_future = RequestFuture.newFuture();

    login(/*omitted for brevity*/, err_future, future);
    return loginResponseHandler(err_future, future);
} // Actual code has login/loginResponseHandler in separate class+file, and more^

But looking at the debug log, I get the error (from somewhere else, not where I'm debugging):

08-24 23:17:13.228 7255-7288 E/Volley: [177] BasicNetwork.performRequest: Unexpected response code 400 for http://192.168.5.3:3003/v1/api/auth

How am I meant to handle errors? - Also is there a reference scaffold to work from?

Community
  • 1
  • 1
A T
  • 13,008
  • 21
  • 97
  • 158
  • Main question is why? why you wana use Volley if you are using `RequestFuture.get` ? it doesn't makes sens ... – Selvin Aug 24 '16 at 13:42
  • Volley is just for async queries from main thread. Use plain `URL` or something in `doInBackground` – Serg Aug 24 '16 at 13:50
  • Selvin: Because in testing and in other areas of the code that `AuthClient` is used, I don't need the result straight away. Here I do [right?] so now I need `doInBackground` to run a function and return the result. But maybe there's another way! - Regardless my implementation isn't working. – A T Aug 24 '16 at 13:54
  • @Serg: Volley does a bunch of caching things also which is useful. – A T Aug 24 '16 at 13:55

2 Answers2

6

first of all

VolleyError err_response = err_future.get();

it is not good if the request does not have an error you will get some cast exception.

actually you don't need 2 Futures as RequestFuture implements both Response and Error Listeners.

So to the point.

future.get()

returns the Result. If an error occurs then it throws and Execution exception which could containf VolleyError exception, which may contain some network data if any is received. All you need to do is to cast to inner excetion to VolleyError and get the data in your catch statement.

try {
    JSONObject response = future.get();
    System.out.println("response.toString() = " + response.toString());
    return true;
} catch (InterruptedException | ExecutionException e) {
    if (VolleyError.class.isAssignableFrom(e.getCause().getClass())) {
        VolleyError ve = (VolleyError) e.getCause();
        System.err.println("ve = " + ve.toString());
        if (ve.networkResponse != null) {
            System.err.println("ve.networkResponse = " +
                               ve.networkResponse.toString());
            System.err.println("ve.networkResponse.statusCode = " +
                               ve.networkResponse.statusCode);
            System.err.println("ve.networkResponse.data = " +
                               new String(ve.networkResponse.data));
            return false;
        }
    }
}

Update

As regards to 'the best client design' it depends on the app design choices you have made. However there few important things for errors you must know:

Errors can be 3 main types: protocol/network errors with network response or without and other errors purely java and related to handling request/response the most common of which is parsing data.

There 2 main approaches to handle errors having that they are not coming from your code.

  1. use custom retry policy
  2. inform the user for the error

Normally you may use the first one for some network and 5xx errors and then have some message handler to let the user know hat has happened.

Again, how you implement this really depends on your app architecture. I personally use a a lot rxJava and apply some kind of data flow design where I got all the errors coming in one place and then decide what to do with them depending on the request, the error itself and app state.

For my apps I use jus(highly extended volley) with rx-jus module and RxQueueHub class based on rxHub so i get all the responses and errors in one place available to all my handlers.

kalin
  • 3,546
  • 2
  • 25
  • 31
  • Edited your answer so it now actually 0) compiles, and 1) shows me the data I want. Thanks for putting me in the right direction! - PS: Any tips on client layout, i.e.: scaffold? – A T Aug 28 '16 at 00:25
  • Very informative, butfrom which documentation, or how did you figure out that the Execution exception included VolleyError exception? – Evren Yurtesen Apr 09 '18 at 06:38
0

Ah that was silly, here's the solution:

if (err_future != null) throw err_future.get();

Add after try in loginResponseHandler, replacing:

VolleyError err_response = err_future.get();

Now the error is thrown and caught, so it can be handled programmatically.

EDIT: How do I get the actual network response? - Like status code and the error body? Like: com.android.volley.NetworkResponse

A T
  • 13,008
  • 21
  • 97
  • 158