Android Volley 构建自定义请求

1. 介绍

Volley 提供了基本的 StringRequestJsonObjectRequestJsonArrayRequest 来满足字符串请求和 JSON 请求。

但是有时候这可能还不能满足我们对网络通信的需求。
比如说使用 Gson 库来自动的对响应的 JSON 进行解析等。

此时就需要构建自定义的 Volley 请求。

2. 基本步骤

2.1 继承 Request<T>

自定义请求首先是通过继承基本的 Request<T> 类来实现的。

1
2
3
public class GsonRequst<T> extends Requst<T> {
// class body
}

2.2 实现构造器

然后,我们要实现构造器满足基本的 Request 的构造方法。

1
2
3
4
5
6
7
8
9
10
public GsonRequest(String url, Class<T> clazz,
Response.Listener<T> listener,
Response.ErrorListener errorListener) {

// Fulfill the super constructor
super(Method.GET, url, errorListener);

this.clazz = clazz;
this.listener = listenr;
}

当然,这里作为一个 GsonRequest,仅仅要求满足父类的构造参数是不够的,我们还要指定 Gson 转换的 class 类型,以及必要的 Response Listener

2.3 重载必要方法

为了实现 GsonRequest 我们要重载必要的方法
parseNetworkResponse()deliverNetworkResponse()

  1. 重载 parseNetworkResponse(Response response)

    顾名思义,这个方法是用来解析网络的回复的,对于我们的 GsonRequest,我们首先要将网络的回复(二进制流)转换成 JSON,然后由 Gson 解析成相应的类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Override
    protected Response<T> parseNetworkResponse(
    NetworkResponse response) {
    try {
    String json = new String(response.data,
    HttpHeaderParser.parseCharset(response.headers));
    return Response.success(gson.fromJson(json, clazz),
    HttpHeaderParser.parseCacheHeaders(response));
    }
    // handle errors
    ...
    }
  2. 重载 deliverNetworkResponse(T response)

    这个方法是将 parseNetworkResponse()解析结果发送给我们的 Response listener 的。
    所以,代码较为简单,直接把解析出来的 response 传给 listener 的回调方法即可。

    1
    2
    3
    4
    @Override
    protected void deliverResponse(T response) {
    listener.onResponse(response);
    }

2.4 完整示例

完整的可用代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;

/**
* Make a GET request and return a parsed object from JSON.
*
* @param url URL of the request to make
* @param clazz Relevant class object, for Gson's reflection
* @param headers Map of request headers
*/
public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}

@Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}

@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}

3. 关于 POST 方法

以上的例子是基于 GET 方法来介绍的,如果使用 POST 方法则略有不同。

仍然以 GsonRequest 举例子:

  1. 首先,我们需要接受一个新的参数用来承接 POST 的实例对象
  2. 使用 Gson 将对象序列化成 JSON 字符串
  3. 将 JSON 字符串转换成 Params 或者直接 POST JSON 字符串

此时,我们需要重载几个新方法。

3.1 重载 getParams()getBody() 方法

getParams() 方法实质上会在 getBody() 方法中被调用
如果你只需要 POST 简单的 Params 形式(key=val&another_key=another_val),那么重载 getParams() 方法即可。

但如果普通的 Params 形式无法满足需要,那么就需要重载 getBody() 方法,其返回的内容会成为 HTTP POST 报文中的 Body。

注意:两个方法只能选择重载其中一个

3.1.1 重载 getParams() 方法

一般的 POST 使用,我们重载此方法即可,该方法返回类型是 Map<Sting, String>

GsonRequest 中,我们只需要使用 Gson 将需要 POST 的对象序列化成 JSON,随后将 JSON 反序列化成 Map<String, String> 即可。
Object -> JSON -> Map<String, String>

1
2
3
4
5
6
7
8
9
@Override Map<String, String> getParmas() {
String json = gson.toJson(mPostObject);

// Use TypeToken to avoid the unchecked cast
// and the floating number converted from primitive integer
return gson.fromJson(
json,
new TypeToken<Map<String, String>>(){}.getType());
}

3.1.2 重载 getBody() 方法

如果基本的 getParams() 方法不能满足需要,那么我们可以直接重载 getBody() 方法来实现对 HTTP body 的高度定制。

GsonRequest 中,假如碰到有时需要 POST 带 List 或者数组类型的对象,由于 List 不能被 cast 成 Map<String, String> 的类型,那么就不能使用 getParams() 方法。
应该要使用 getBody() 方法。

注意 getBody() 返回的是 byte[] 类型,我们需要使用 StringgetBytes() 方法来将 JSON 转换成二进制流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public byte[] getBody() throws AuthFailureError {

if (mPostObject != null) {
String postJson = gson.toJson(mPostObject);

try {
return postJson.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException e) {
VolleyLog.wtf(
"Unsupported Encoding while trying to get the byte of %s using %s",
postJson, PROTOCOL_CHARSET);
}
}

return null;
}

3.2 重载 getBodyContentType() 方法

为了我们的 POST body 能被成功解析,我们还需要重载 getBodyContentType() 方法来指定我们 POST 的数据的类型

1
2
3
4
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}