Skip to content

Instantly share code, notes, and snippets.

@sarahsnow1
Created May 29, 2013 16:04
Show Gist options
  • Save sarahsnow1/5671467 to your computer and use it in GitHub Desktop.
Save sarahsnow1/5671467 to your computer and use it in GitHub Desktop.
An API client for the Volley networking library modelled after AFNetworking
package com.eatify.Eatify.api;
public class EatifyApiClient extends VolleyApiClient {
private static volatile EatifyApiClient instance;
public static EatifyApiClient shared() {
if (instance == null) {
synchronized (EatifyApiClient.class) {
if (instance == null) {
instance = new EatifyApiClient();
}
}
}
return instance;
}
public EatifyApiClient() {
super();
mBaseUrl = "http://eatify-backend.herokuapp.com";
mDefaultHeaders.put("Accept", "application/json");
}
}
private void search(String query) {
Map<String, String> params = new HashMap<String, String>();
params.put("count", "10");
params.put("name", query);
EatifyApiClient.shared().get("/restaurants/autocomplete", params, new Response.Listener<StringResponse>() {
@Override
public void onResponse(StringResponse response) {
textView.setText(response.getResponse());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
textView.setText("Error: "+error.getLocalizedMessage());
}
});
}
}
import com.android.volley.NetworkResponse;
public class StringResponse extends NetworkResponse {
protected String response;
public StringResponse(NetworkResponse networkResponse) {
super(networkResponse.statusCode, networkResponse.data, networkResponse.headers, networkResponse.notModified);
this.response = new String(networkResponse.data);
}
public String getResponse() {
return response;
}
}
import android.content.Context;
import com.android.volley.*;
import com.android.volley.toolbox.Volley;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class VolleyApiClient {
protected RequestQueue mRequestQueue;
// Subclass should configure
protected String mBaseUrl = null;
protected Map<String, String> mDefaultHeaders = new HashMap<String, String>();
private static volatile VolleyApiClient instance;
public static VolleyApiClient shared() {
if (instance == null) {
synchronized (VolleyApiClient.class) {
if (instance == null) {
instance = new VolleyApiClient();
}
}
}
return instance;
}
public void start(Context context) {
mRequestQueue = Volley.newRequestQueue(context);
}
public RequestQueue getRequestQueue() {
if (mRequestQueue != null) {
return mRequestQueue;
} else {
throw new IllegalStateException("RequestQueue not initialized");
}
}
protected String fullyQualifiedUrlForPath(String path) {
if (mBaseUrl != null) {
return mBaseUrl+path;
}
else {
throw new IllegalStateException("BaseUrl not initialized");
}
}
private String flattenParams(Map<String, String> inParameters) {
StringBuilder params = null;
Iterator it = inParameters.entrySet().iterator();
while (it.hasNext()) {
if (params == null) {
params = new StringBuilder();
params.append("?");
}
else {
params.append("&");
}
Map.Entry pairs = (Map.Entry)it.next();
String key = null;
String val = null;
try {
key = URLEncoder.encode((String) pairs.getKey(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
val = URLEncoder.encode((String) pairs.getValue(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
params.append(key+"="+val);
}
return params.toString();
}
public Request request(int method, String path, Map<String, String> inParameters, Map<String, String> inHeaders, Response.Listener onSuccess, Response.ErrorListener onFailure) {
final Response.Listener listener = onSuccess;
final Map<String, String> params = inParameters;
final Map<String, String> headers = inHeaders;
if (method == Request.Method.GET && params != null) {
path = path+flattenParams(params);
}
Request request = new Request(method, fullyQualifiedUrlForPath(path), onFailure) {
@Override
protected Response parseNetworkResponse(NetworkResponse networkResponse) {
StringResponse stringResponse = new StringResponse(networkResponse);
return Response.success(stringResponse, getCacheEntry());
}
@Override
protected void deliverResponse(Object o) {
listener.onResponse(o);
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> allHeaders = new HashMap<String, String>();
allHeaders.putAll(super.getHeaders());
if (mDefaultHeaders != null) {
allHeaders.putAll(mDefaultHeaders);
}
if (headers!= null) {
allHeaders.putAll(headers);
}
return allHeaders;
}
};
mRequestQueue.add(request);
return request;
}
public Request get(String path, Map<String, String> parameters, Response.Listener onSuccess, Response.ErrorListener onFailure) {
return request(Request.Method.GET, path, parameters, null, onSuccess, onFailure);
}
}
@ficusk
Copy link

ficusk commented Jun 1, 2013

I've used a similar pattern in a few apps I've worked on with Volley, but instead of making an abstract API pattern, I think it makes for nicer code to actually make something like a Java version of your REST API.

Something like this:

public class EatifyApi {
  public Request<?> autocomplete(String query, int count, Listener<String> listener, ErrorListener errorListener) {
    // You'd want to urlencode query here.
    String url = BASE_URL + "/restaurants/autocomplete?query=" + query + "&count=" + count;
    return mRequestQueue.add(
        new StringRequest(url, listener, errorListener));
  }
}

This is a simplified case, you might need to have your own Request subclass to do the header stuff. I'm thinking we should probably add an API in Volley to add your own headers to any baseline request so you don't have to do that, but for the time being...

Also, more of a personal style thing but I'd drop all the synchronized stuff and instead assert that you're on the main thread. Don't lock unless you really have to!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment