前言

博客将按照下面的步骤介绍Volley的重新封装:
1.OkHttp3的关于Volley的HttpStack实现
2.HttpRequest的实现和HttpListener回调监听的封装
3.Volley原始的Request的Wrap
4.各种方式的请求的重新实现
5.统一请求的实现
6.使用

所需依赖:

1
2
3
4
compile 'com.android.volley:volley:1.0.0'
compile 'com.squareup.okio:okio:1.7.0'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.google.code.gson:gson:2.6.2'

OkHttp3Stack的关于Volley的实现

这个是应该是比较简单的,关于OkHttp3Stack的实现在github上面有实现,本博客里面的实现在我的Github上面。
由于代码比较长,而且这个也不是这篇博客的重点,大家需要的话可以去我的Github查看。

HttpRequest的实现和HttpListener回调监听的封装

HttpRequest的实现

通过查看Volley的源代码我们会发现,Volley的Request的构造方法是这样写的:

1
2
3
4
5
6
7
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}

请求的参数有一些通过构造方法传递,而另外一些参数是通过Request里面的很多的get方法得到的,比如post请求的时候的参数是通过Request类里面的getParams()实现的,而我们需要做的就是如果需要post请求,那么就重写请求类,覆盖里面的getParams方法。

1
2
3
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}

会发现这样并不利于统一的调度,那其实在构造一个请求的时候,参数是不固定的,而且有的需要,有的不需要,这个时候,我们可以通过Builder模式来构造请求,可以进行如下的封装。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package com.yong.volleyok;
public class HttpRequest {

private Builder mBuilder;

private HttpRequest(Builder builder) {
this.mBuilder = builder;
}

public Map<String, String> getHeaders() {
return mBuilder.headMaps;
}

public int getMethod() {
return mBuilder.method;
}

public Map<String, String> getParams() {
return mBuilder.params;
}

public Request.Priority getPriority() {
return mBuilder.priority;
}

public String getContentType() {
return mBuilder.contentType;
}

public String getParamsEncodeing() {
return mBuilder.paramsEncodeing;
}

public RetryPolicy getRetryPolicy() {
return mBuilder.retryPolicy;
}

public String getUrl() {
return mBuilder.url;
}

public static final class Builder {

String paramsEncodeing = "UTF-8";
String url;
int method = Request.Method.GET;
Request.Priority priority = Request.Priority.NORMAL;
String contentType = "application/x-www-form-urlencoded; charset=utf-8";
// 请求头
Map<String, String> headMaps = new HashMap<>();
// 参数
Map<String, String> params = new HashMap<>();

// 超时以及重连次数
RetryPolicy retryPolicy = new DefaultRetryPolicy(10000, 2, 1.0F);

public Builder(String url) {
this.url = url;
}

/**
* 增加 Http 头信息
*
* @param key key
* @param value value
* @return
*/
public Builder addHeader(String key, String value) {
this.headMaps.put(key, value);
return this;
}

/**
* 增加 Http 头信息
*
* @param headers
* @return
*/
public Builder addheader(Map<String, String> headers) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
this.headMaps.put(entry.getKey(), entry.getValue());
}
return this;
}

/**
* 设置 Http 请求方法
*
* @param method {@link Request.Method}
* @return
*/
public Builder setMethod(int method) {
this.method = method;
return this;
}

/**
* 增加请求参数
*
* @param key key
* @param value value
* @return
*/
public Builder addParam(String key, Object value) {
this.params.put(key, String.valueOf(value));
return this;
}

/**
* 增加请求参数
*
* @param params map<string, object>
* @return
*/
public Builder addParam(Map<String, Object> params) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
this.params.put(entry.getKey(), String.valueOf(entry.getValue()));
}
return this;
}

/**
* 设置请求优先级
*
* @param priority {@link Request.Priority}
* @return
*/
public Builder setPriority(Request.Priority priority) {
this.priority = priority;
return this;
}

/**
* 设置文本类型
*
* @param contentType
* @return
*/
public Builder setContentType(String contentType) {
this.contentType = contentType;
return this;
}

/**
* 设置超时以及重连次数
*
* @param initialTimeoutMs 超时时间
* @param maxNumRetries 重连次数
* @param backoffMultiplier
* @return
*/
public Builder setRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
this.retryPolicy = new DefaultRetryPolicy(initialTimeoutMs, maxNumRetries, backoffMultiplier);
return this;
}

/**
* 构建 HttpRequest
*
* @return
*/
public HttpRequest build() {
return new HttpRequest(this);
}
}
}

我们构造这样的一个请求的类,然后在请求的时候可以通过这个类,去构建请求的时候需要的一些参数。这个类很简单就不用详细的讲解了。具体的怎么使用这个类去构造请求,我们会在Wrap Volley的Request的时候详细的说明。

HttpListener的封装

其实就是回调的封装,在Volley里面是使用了两个接口来做的,这里统一成一个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.yong.volleyok;
public interface HttpListener<T> {
/**
* 服务器响应成功
*
* @param result 响应的理想数据。
*/
void onSuccess(T result);

/**
* 网络交互过程中发生错误
*
* @param error {@link VolleyError}
*/
void onError(VolleyError error);
}

将成功和失败的回调封装到一个方法里面了。

Request的Wrap

为了以后能更好的升级的考虑,我们最好是不采用直接改源代码的方式,所以我们只能对原始的请求类Request类进行Wrap,然后我们自定义请求类继承这个Wrap的类。先贴代码,然后讲解。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.yong.volleyok.request;
public abstract class RequestWrapper<T> extends com.android.volley.Request<T> {
protected HttpRequest mHttpRequest;
protected HttpListener<T> mHttpListener;

public RequestWrapper(HttpRequest httpRequest, HttpListener<T> listener) {
// 这里不需要错误的监听,下面已经做了处理
super(httpRequest.getMethod(), httpRequest.getUrl(), null);
this.mHttpRequest = httpRequest;
this.mHttpListener = listener;
}

/**
* 得到url,这里get方法作处理,把参数都拼接上去
*
* @return
*/
@Override
public String getUrl() {
// 当get的时候做处理,把参数都连接起来
try {
if (getMethod() == Method.GET &&
(getParams() != null && getParams().size() != 0)) {
String encodedParams = getEncodedUrlParams();
String extra = "";
if (encodedParams != null && encodedParams.length() > 0) {
if (!mHttpRequest.getUrl().endsWith("?")) {
extra += "?";
}
extra += encodedParams;
}
return mHttpRequest.getUrl() + extra;
}
} catch (AuthFailureError e) {
}
return mHttpRequest.getUrl();

}

/**
* 拼接get请求的参数的拼接
*
* @return
* @throws AuthFailureError
*/
public String getEncodedUrlParams() throws AuthFailureError {
StringBuilder encodedParams = new StringBuilder();
String paramsEncoding = getParamsEncoding();
Map<String, String> params = getParams();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
if (null == entry.getValue()) {
continue;
}
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString();
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}

/**
* 得到请求头
*
* @return
* @throws AuthFailureError
*/
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return mHttpRequest.getHeaders();
}

/**
* 请求参数
*
* @return
* @throws AuthFailureError
*/
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return mHttpRequest.getParams();
}

/**
* 请求的ContentType
*
* @return
*/
@Override
public String getBodyContentType() {
return mHttpRequest.getContentType();
}

/**
* 请求的优先级,这里RequestQueue里面会根据这个把请求进行排序
*
* @return
*/
@Override
public Priority getPriority() {
return mHttpRequest.getPriority();
}

/**
* 设置请求时长,请求失败之后的次数
*
* @return
*/
@Override
public RetryPolicy getRetryPolicy() {
return mHttpRequest.getRetryPolicy();
}

/**
* 请求成功
*
* @param response The parsed response returned by
*/
@Override
protected void deliverResponse(T response) {
if (mHttpListener != null) {
mHttpListener.onSuccess(response);
}
}

/**
* 请求失败
*
* @param error Error details
*/
@Override
public void deliverError(VolleyError error) {
if (mHttpListener != null) {
mHttpListener.onError(error);
}
}
}

这里最重要的就是这个类了,下面详细的说一下,封装的过程:
我们在前面定义了HttpRequest和HttpListener类就是为了在这里使用,在构造方法里面把这两个传递进来,然后请求需要的参数通过HttpRequest的一系列的get方法获取,请求最终的回调通过HttpListener传递出去。

首先来看HttpListener的最终的两个回调的方法:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void deliverResponse(T response) {
if (mHttpListener != null) {
mHttpListener.onSuccess(response);
}
}
@Override
public void deliverError(VolleyError error) {
if (mHttpListener != null) {
mHttpListener.onError(error);
}
}

这两个方法一个是请求成功的方法,另外一个是请求失败的方法,我们通过HttpListener把最后的结果抛出去,这里可以统一实现,不需要子请求类再去实现了。

再看其余的一些方法,getUrl方法是请求的url,但是我们在封装请求的时候不管是get还是post都是把参数放置getParams方法里面返回的,get请求是直接使用url拼接参数的,所以需要对这个方法进行重写,这样,才能保证get请求能有参数。

getHeaders是请求头,这里直接使用HttpRequest获取到。

getParams时请求的参数,也是直接通过HttpRequest拿到。

其余的方法都是大同小异,总体来说这个封装也比较简单的。

各种方式的请求的重新实现

上面对Request进行了重新的封装之后,我们只需要继承RequestWrapper即可,并且,需要我们实现的方法也只有一个了,parseNetworkResponse。由于我们队Request进行了封装,所以Volley自己带的几个请求,如JsonRequest,StringRequest等,都需要重写,继承RequestWrapper,但是,经过我们的封装,重写不会很麻烦。
这里只举两个例子,一个ByteRequest:

1
2
3
4
5
6
7
8
9
10
11
12
package com.yong.volleyok.request;
public class ByteRequest extends RequestWrapper<byte[]> {

public ByteRequest(HttpRequest httpRequest, HttpListener<byte[]> listener) {
super(httpRequest, listener);
}

@Override
protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
}
}

可以看到经过我们的封装之后,实现变得非常简单了。

GsonRequest:

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
package com.yong.volleyok.request;
public class GsonRequest<T> extends RequestWrapper<T> {

private static Gson mGson = new Gson();
private Class<T> mClass;
private TypeToken<T> mTypeToken;

public GsonRequest(Class<T> tClass, HttpRequest httpRequest, HttpListener<T> listener) {
this(tClass, null, httpRequest, listener);
}

public GsonRequest(Class<T> tClass, TypeToken<T> typeToken,
HttpRequest httpRequest, HttpListener<T> listener) {
super(httpRequest, listener);
mClass = tClass;
mTypeToken = typeToken;
}

@SuppressWarnings("unchecked")
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data, HttpHeaderParser.parseCharset(response.headers, getParamsEncoding()));
if (mTypeToken == null) {
return Response.success(
mGson.fromJson(json, mClass), HttpHeaderParser.parseCacheHeaders(response));
} else {
return (Response<T>) Response.success(
mGson.fromJson(json, mTypeToken.getType()), HttpHeaderParser.parseCacheHeaders(response));
}
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}

这里只贴出这两个方法,还有的方法可以看Github

统一请求的实现

请求都封装好了,现在只有调用了,针对封装的6种请求可以封装一个接口。

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
package com.yong.volleyok;
public interface IHttpClient {

/**
* byte请求
*/
Request byteRequest(HttpRequest httpRequest, HttpListener<byte[]> listener, Object tag);

/**
* String请求
*/
Request stringRequest(HttpRequest httpRequest, HttpListener<String> listener, Object tag);

/**
* gzip请求
*/
Request gZipRequest(HttpRequest httpRequest, HttpListener<String> listener, Object tag);

/**
* JsonObject请求
* @return
*/
Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener<JSONObject> listener, Object tag);

/**
* JsonArray请求
*/
Request jsonArrayRequest(String requestBody, HttpRequest httpRequest, HttpListener<JSONArray> listener, Object tag);

/**
* Gson请求,可以映射Model
*/
<T> Request gsonRequest(Class<T> tClass, TypeToken<T> typeToken, HttpRequest httpRequest, HttpListener<T> listener, Object tag);
}

然后再写一个具体的实现类即可。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.yong.volleyok;
public class HttpClient implements IHttpClient {

private static HttpClient INSTANCE;
private static final int[] sLock = new int[0];

private final RequestQueue mRequestQueue;
private final Context mContext;

private HttpClient(Context context) {
mContext = context;
mRequestQueue = Volley.newRequestQueue(context,
new OkHttp3Stack(new OkHttpClient()));
}

/**
* 这里使用Application的Context
*
* @param context
* @return
*/
public static HttpClient getInstance(Context context) {
if (null == INSTANCE) {
synchronized (sLock) {
if (null == INSTANCE) {
INSTANCE = new HttpClient(context);
}
}
}
return INSTANCE;
}

/**
* 添加请求
*
* @param request
*/
public void addRequest(Request request, Object tag) {
if (tag != null) {
request.setTag(tag);
}
mRequestQueue.add(request);
}

/**
* 取消请求
*
* @param tag
*/
public void cancelRequest(Object tag) {
mRequestQueue.cancelAll(tag);
}

public Request ByteRequest(HttpRequest httpRequest, HttpListener<byte[]> listener) {
return byteRequest(httpRequest, listener, null);
}

@Override
public Request byteRequest(HttpRequest httpRequest, HttpListener<byte[]> listener, Object tag) {
ByteRequest request = new ByteRequest(httpRequest, listener);
addRequest(request, tag);
return request;
}

public Request stringRequest(HttpRequest httpRequest, HttpListener<String> listener) {
return stringRequest(httpRequest, listener, null);
}

@Override
public Request stringRequest(HttpRequest httpRequest, HttpListener<String> listener, Object tag) {
StringRequest request = new StringRequest(httpRequest, listener);
addRequest(request, tag);
return request;
}

public Request gZipRequest(HttpRequest httpRequest, HttpListener<String> listener) {
return gZipRequest(httpRequest, listener, null);
}

@Override
public Request gZipRequest(HttpRequest httpRequest, HttpListener<String> listener, Object tag) {
GZipRequest request = new GZipRequest(httpRequest, listener);
addRequest(request, tag);
return request;
}

public Request jsonObjectRequest(HttpRequest httpRequest, HttpListener<JSONObject> listener) {
return jsonObjectRequest(null, httpRequest, listener);
}

public Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener<JSONObject> listener) {
return jsonObjectRequest(requestBody, httpRequest, listener, null);
}

@Override
public Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener<JSONObject> listener, Object tag) {
JsonObjectRequest request = new JsonObjectRequest(requestBody, httpRequest, listener);
addRequest(request, tag);
return request;
}

public Request jsonArrayRequest(HttpRequest httpRequest, HttpListener<JSONArray> listener) {
return jsonArrayRequest(httpRequest, listener, null);
}

public Request jsonArrayRequest(HttpRequest httpRequest, HttpListener<JSONArray> listener, Object tag) {
return jsonArrayRequest(null, httpRequest, listener, tag);
}

@Override
public Request jsonArrayRequest(String requestBody, HttpRequest httpRequest, HttpListener<JSONArray> listener, Object tag) {
JsonArrayRequest request = new JsonArrayRequest(requestBody, httpRequest, listener);
addRequest(request, tag);
return request;
}

public <T> Request gsonRequest(Class<T> tClass, HttpRequest httpRequest, HttpListener<T> listener) {
return gsonRequest(tClass, httpRequest, listener, null);
}

public <T> Request gsonRequest(Class<T> tClass, HttpRequest httpRequest, HttpListener<T> listener, Object tag) {
return gsonRequest(tClass, null, httpRequest, listener, tag);
}

@Override
public <T> Request gsonRequest(Class<T> tClass, TypeToken<T> typeToken,
HttpRequest httpRequest, HttpListener<T> listener, Object tag) {
GsonRequest<T> request = new GsonRequest<T>(tClass, typeToken, httpRequest, listener);
addRequest(request, tag);
return request;
}
}

使用方式

当然使用也非常简单,看HttpClient就知道有哪些方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mResult = (TextView) findViewById(R.id.result);
mHttpClient = HttpUtil.getHttpClient();
HttpRequest request = new HttpRequest.Builder("http://www.mocky.io/v2/571b3c270f00001a0faddfcc")
.setMethod(Request.Method.GET)
.build();
mHttpClient.stringRequest(request, new HttpListener<String>() {
@Override
public void onSuccess(String result) {
Log.e("TAG", result);
mResult.setText(result);
}

@Override
public void onError(VolleyError error) {
mResult.setText(error.getMessage());
}
});

库和Demo地址:
https://github.com/qingyongai/VolleyOkExtension