Implementing a RESTful API Wrapper in Android (Part II)

In part 1 I talked about how to implement an API call wrapper in iOS using swift, now let’s move on to Android.

I use the popular Android library Volley, an HTTP library that makes networking for Android apps easier and most importantly, faster. You can add volley by simply adding compile ‘com.android.volley:volley:1.0.0’ to your app gradle file. To create an API wrapper around volley and simplify the http requests, I created the class QueryApi :

public class QueryAPI {
    private String hostname = "http://example.com/";

    private SessionManager session;

    private static QueryAPI mInstance = new QueryAPI();

    //Create a singleton
    public static synchronized QueryAPI getInstance() {
        return mInstance;
    }

    //Create an object to return the server's response
    public class ApiResult {
        public Boolean success;
        public String message;
        public Object data;

        //check what kind of data is returned in the json
        public boolean dataIsArray() {
            return (data != null && data instanceof JSONArray);
        }

        public boolean dataIsObject() {
            return (data != null && data instanceof JSONObject);
        }

        public boolean dataIsInteger() {
            return (data != null && data instanceof Integer);
        }
        
        //return the data properly casted
        public JSONArray getDataAsArray() {
            if (this.dataIsArray()) {
                return (JSONArray) this.data;
            } else {
                return null;
            }
        }

        public JSONObject getDataAsObject() {
            if (this.dataIsObject()) {
                return (JSONObject) this.data;
            } else {
                return null;
            }
        }

        public Integer getDataAsInteger() {
            if (this.dataIsInteger()) {
                return (Integer) this.data;
            } else {
                return null;
            }
        }

    }
    
    //create an interface with a callback method
    public interface ApiResponse<T> {
        public void onCompletion(T result);
    }

    //Create a get request with the url to query, and a callback
    public void RequestApi(String url, final ApiResponse<ApiResult> completion) {
        Log.v("Performing request: ", url);
        JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.GET, hostname + url, (JSONObject) null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        ApiResult res = new ApiResult();
                        Log.v("RequestApi Response", response.toString());
                        //Log.v("Data: ", response.toString());
                        try {

                            Boolean success = response.getBoolean("success");
                            try {
                                res.data = response.getJSONArray("data");
                            } catch (JSONException e) {
                                Log.v("exception catch", e.getMessage());
                                try {
                                    res.data = response.getJSONObject("data");
                                } catch (JSONException x) {
                                    Log.v("exception catch", x.getMessage());
                                    res.data = response.getInt("data");
                                }
                            }

                            res.success = success;

                        } catch (JSONException e) {
                            e.printStackTrace();
                            res.success = false;
                        }
                        completion.onCompletion(res);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                ApiResult res = new ApiResult();
                res.success = false;
                completion.onCompletion(res);
                displayVolleyResponseError(error);
            }
        }
        );

        //In case the server is a bit slow and we experience timeout errors﹕ error => com.android.volley.TimeoutError
        jsonRequest.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));

        AppController.getInstance().addToRequestQueue(jsonRequest);

    }

You might have noticed at the end that we are adding our request to a request queue handled by the AppController class. If you don’t have such a class, follow the tutorial about creating a singleton in Android to handle cookies and volley requests.

So far we have just a simple get request. What if you want to add a wrapper for the POST requests too? In this case, can just add this function inside QueryAPI:

/**
     * @param url : url to query
     * @param params : parameters to pass in the post request
     * @param completion
     */
public void RequestApiPOST (String url, final Map<String, String> params, final ApiResponse<ApiResult> completion )
    {
        StringRequest strRequest = new StringRequest(Request.Method.POST, hostname+url,
                new Response.Listener<String>()
                {
                    @Override
                    public void onResponse(String responseString)
                    {
                        if (responseString != null) {
                            Log.d("POSTResponse", responseString);
                        }
                        JSONObject response = null;
                        ApiResult res = new ApiResult();
                        res.success = false;
                        try {
                            response = new JSONObject(responseString);

                            Log.v("POST Response", response.toString());
                            try {

                                Boolean success = response.getBoolean("success");
                                try {
                                    res.data = response.getJSONArray("data");
                                }
                                catch (JSONException e)
                                {
                                    Log.v("exception catch", e.getMessage());
                                    try {
                                        res.data = response.getJSONObject("data");
                                    }
                                    catch (JSONException x)
                                    {
                                        Log.v("exception catch", x.getMessage());
                                        try {
                                            res.data = response.getInt("data");
                                        }
                                        catch (JSONException z)
                                        {
                                            res.data = response.getString("data");
                                        }
                                    }
                                }

                                res.success = success;

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }


                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                        completion.onCompletion(res);
                    }
                },
                new Response.ErrorListener()
                {
                    @Override
                    public void onErrorResponse(VolleyError error)
                    {
                        Log.v("error post:", error.toString());
                        displayVolleyResponseError(error);
                    }
                })

        {
            @Override
            protected Map<String, String> getParams()
            {
                return params;
            }
        };

        //Added for fb connect which timeout
        strRequest.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));

        AppController.getInstance().addToRequestQueue(strRequest);

Finally, add to the class the error handling function:

private void displayVolleyResponseError(VolleyError error) {
        if (error instanceof TimeoutError || error instanceof NoConnectionError) {
            Toast.makeText(AppController.getContext(),
                    AppController.getContext().getString(R.string.error_network_timeout),
                    Toast.LENGTH_LONG).show();
        } else if (error instanceof AuthFailureError) {
            Toast.makeText(AppController.getContext(),
                    AppController.getContext().getString(R.string.error_auth_failure),
                    Toast.LENGTH_LONG).show();
        } else if (error instanceof ServerError) {
            Toast.makeText(AppController.getContext(),
                    AppController.getContext().getString(R.string.error_server),
                    Toast.LENGTH_LONG).show();
        } else if (error instanceof NetworkError) {
            Toast.makeText(AppController.getContext(),
                    AppController.getContext().getString(R.string.error_network),
                    Toast.LENGTH_LONG).show();
        } else if (error instanceof ParseError) {
            Toast.makeText(AppController.getContext(),
                    AppController.getContext().getString(R.string.error_parse),
                    Toast.LENGTH_LONG).show();
        }
    }

You now have all the basics http request ready, we can now add our api calls.
Still in queryApi, we can use the wrapper requestApi or requestApiPOST as follow whenever we need to make a request to the server:

public void user(String userId, final ApiResponse<ModelUser> completion) {

        String url = "owapi/user/profile/"+userId;
        final ModelUser user = new ModelUser();

        this.RequestApi(url, new ApiResponse<ApiResult>() {
            @Override
            public void onCompletion(ApiResult res) {
                if (res.success && res.dataIsObject()) {
                    JSONObject userObj = res.getDataAsObject();
                    ModelUser user = new ModelUser();
                    user = new Gson().fromJson(userObj.toString(), ModelUser.class);
                    completion.onCompletion(user);
                } else {
                    completion.onCompletion(user);
                }
            }
        });

    }

In this code, you can see that I am using the awesome Gson library to parse the json directly into my user model. Gson is a Java serialization/deserialization library that can convert Java Objects into JSON and back, and which you can install by adding compile ‘com.google.code.gson:gson:2.3.1’ to your gradle file.

You can then use this method anywhere in your code to return your object model:

QueryAPI query = new QueryAPI();
query.user(userId, new QueryAPI.ApiResponse<ModelUser>() {
      @Override
      public void onCompletion(ModelUser user) {
         //Do something with the user object
      }
    });

 

Next: How to do send images through multipart request with Volley