1# Implement a custom request 2 3This lesson describes how to implement your own custom request types, for types that 4don't have out-of-the-box Volley support. 5 6## Write a custom request 7 8Most requests have ready-to-use implementations in the toolbox; if your response is a string, 9image, or JSON, you probably won't need to implement a custom `Request`. 10 11For cases where you do need to implement a custom request, this is all you need 12to do: 13 14- Extend the `Request<T>` class, where `<T>` represents the type of parsed response 15 the request expects. So if your parsed response is a string, for example, 16 create your custom request by extending `Request<String>`. See the Volley 17 toolbox classes `StringRequest` and `ImageRequest` for examples of 18 extending `Request<T>`. 19- Implement the abstract methods `parseNetworkResponse()` 20 and `deliverResponse()`, described in more detail below. 21 22### parseNetworkResponse 23 24A `Response` encapsulates a parsed response for delivery, for a given type 25(such as string, image, or JSON). Here is a sample implementation of 26`parseNetworkResponse()`: 27 28*Kotlin* 29 30```kotlin 31override fun parseNetworkResponse(response: NetworkResponse?): Response<T> { 32 return try { 33 val json = String( 34 response?.data ?: ByteArray(0), 35 Charset.forName(HttpHeaderParser.parseCharset(response?.headers))) 36 Response.success( 37 gson.fromJson(json, clazz), 38 HttpHeaderParser.parseCacheHeaders(response)) 39 } 40 // handle errors 41} 42``` 43 44*Java* 45 46```java 47@Override 48protected Response<T> parseNetworkResponse(NetworkResponse response) { 49 try { 50 String json = new String(response.data, 51 HttpHeaderParser.parseCharset(response.headers)); 52 return Response.success(gson.fromJson(json, clazz), 53 HttpHeaderParser.parseCacheHeaders(response)); 54 } 55 // handle errors 56} 57``` 58 59Note the following: 60 61- `parseNetworkResponse()` takes as its parameter a `NetworkResponse`, which 62 contains the response payload as a byte[], HTTP status code, and response headers. 63- Your implementation must return a `Response<T>`, which contains your typed 64 response object and cache metadata or an error, such as in the case of a parse failure. 65 66If your protocol has non-standard cache semantics, you can build a `Cache.Entry` 67yourself, but most requests are fine with something like this: 68 69*Kotlin* 70 71```kotlin 72return Response.success(myDecodedObject, 73 HttpHeaderParser.parseCacheHeaders(response)) 74``` 75 76*Java* 77 78```java 79return Response.success(myDecodedObject, 80 HttpHeaderParser.parseCacheHeaders(response)); 81``` 82 83Volley calls `parseNetworkResponse()` from a worker thread. This ensures that 84expensive parsing operations, such as decoding a JPEG into a Bitmap, don't block the UI 85thread. 86 87### deliverResponse 88 89Volley calls you back on the main thread with the object you returned in 90`parseNetworkResponse()`. Most requests invoke a callback interface here, 91for example: 92 93*Kotlin* 94 95```kotlin 96override fun deliverResponse(response: T) = listener.onResponse(response) 97``` 98 99*Java* 100 101```java 102protected void deliverResponse(T response) { 103 listener.onResponse(response); 104} 105``` 106 107## Example: GsonRequest 108 109[Gson](https://github.com/google/gson) is a library for converting 110Java objects to and from JSON using reflection. You can define Java objects that have the 111same names as their corresponding JSON keys, pass Gson the class object, and Gson will fill 112in the fields for you. Here's a complete implementation of a Volley request that uses 113Gson for parsing: 114 115*Kotlin* 116 117```kotlin 118/** 119 * Make a GET request and return a parsed object from JSON. 120 * 121 * @param url URL of the request to make 122 * @param clazz Relevant class object, for Gson's reflection 123 * @param headers Map of request headers 124 */ 125class GsonRequest<T>( 126 url: String, 127 private val clazz: Class<T>, 128 private val headers: MutableMap<String, String>?, 129 private val listener: Response.Listener<T>, 130 errorListener: Response.ErrorListener 131) : Request<T>(Method.GET, url, errorListener) { 132 private val gson = Gson() 133 134 override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders() 135 136 override fun deliverResponse(response: T) = listener.onResponse(response) 137 138 override fun parseNetworkResponse(response: NetworkResponse?): Response<T> { 139 return try { 140 val json = String( 141 response?.data ?: ByteArray(0), 142 Charset.forName(HttpHeaderParser.parseCharset(response?.headers))) 143 Response.success( 144 gson.fromJson(json, clazz), 145 HttpHeaderParser.parseCacheHeaders(response)) 146 } catch (e: UnsupportedEncodingException) { 147 Response.error(ParseError(e)) 148 } catch (e: JsonSyntaxException) { 149 Response.error(ParseError(e)) 150 } 151 } 152} 153``` 154 155*Java* 156 157```java 158public class GsonRequest<T> extends Request<T> { 159 private final Gson gson = new Gson(); 160 private final Class<T> clazz; 161 private final Map<String, String> headers; 162 private final Listener<T> listener; 163 164 /** 165 * Make a GET request and return a parsed object from JSON. 166 * 167 * @param url URL of the request to make 168 * @param clazz Relevant class object, for Gson's reflection 169 * @param headers Map of request headers 170 */ 171 public GsonRequest(String url, Class<T> clazz, Map<String, String> headers, 172 Listener<T> listener, ErrorListener errorListener) { 173 super(Method.GET, url, errorListener); 174 this.clazz = clazz; 175 this.headers = headers; 176 this.listener = listener; 177 } 178 179 @Override 180 public Map<String, String> getHeaders() throws AuthFailureError { 181 return headers != null ? headers : super.getHeaders(); 182 } 183 184 @Override 185 protected void deliverResponse(T response) { 186 listener.onResponse(response); 187 } 188 189 @Override 190 protected Response<T> parseNetworkResponse(NetworkResponse response) { 191 try { 192 String json = new String( 193 response.data, 194 HttpHeaderParser.parseCharset(response.headers)); 195 return Response.success( 196 gson.fromJson(json, clazz), 197 HttpHeaderParser.parseCacheHeaders(response)); 198 } catch (UnsupportedEncodingException e) { 199 return Response.error(new ParseError(e)); 200 } catch (JsonSyntaxException e) { 201 return Response.error(new ParseError(e)); 202 } 203 } 204} 205``` 206 207Volley provides ready-to-use `JsonArrayRequest` and `JsonArrayObject` classes 208if you prefer to take that approach. See [Make a standard request](request.md) for more information. 209