Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling errors in Volley (with futures)?

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?

like image 513
A T Avatar asked Aug 24 '16 13:08

A T


1 Answers

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.

like image 102
kalin Avatar answered Nov 09 '22 15:11

kalin