• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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