博客将按照下面的步骤介绍Volley的重新封装:
1.OkHttp3的关于Volley的HttpStack实现
2.HttpRequest的实现和HttpListener回调监听的封装
3.Volley原始的Request的Wrap
4.各种方式的请求的重新实现
5.统一请求的实现
6.使用
所需依赖:
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的实现在github上面有实现,本博客里面的实现在我的Github上面。
由于代码比较长,而且这个也不是这篇博客的重点,大家需要的话可以去我的Github查看。
通过查看Volley的源代码我们会发现,Volley的Request的构造方法是这样写的:
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方法。
protected Map getParams() throws AuthFailureError {
return null;
}
会发现这样并不利于统一的调度,那其实在构造一个请求的时候,参数是不固定的,而且有的需要,有的不需要,这个时候,我们可以通过Builder模式来构造请求,可以进行如下的封装:
package com.yong.volleyok;
/**
* Project: com.yong.volleyok
* Create Date: 2016/4/22
* Author: qingyong
* Description: 请求的封装,使用Builder模式进行构建
*/
public class HttpRequest {
private Builder mBuilder;
private HttpRequest(Builder builder) {
this.mBuilder = builder;
}
public Map getHeaders() {
return mBuilder.headMaps;
}
public int getMethod() {
return mBuilder.method;
}
public Map 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 headMaps = new HashMap<>();
// 参数
Map 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 headers) {
for (Map.Entry 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
* @return
*/
public Builder addParam(Map params) {
for (Map.Entry 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的时候详细的说明。
其实就是回调的封装,在Volley里面是使用了两个接口来做的,这里统一成一个接口。
package com.yong.volleyok;
/**
* Project: com.yong.volleyok
* Create Date: 2016/4/22
* Author: qingyong
* Description: 回调响应
*/
public interface HttpListener {
/**
* 服务器响应成功
*
* @param result 响应的理想数据。
*/
void onSuccess(T result);
/**
* 网络交互过程中发生错误
*
* @param error {@link VolleyError}
*/
void onError(VolleyError error);
}
将成功和失败的回调封装到一个方法里面了。
为了以后能更好的升级的考虑,我们最好是不采用直接改源代码的方式,所以我们只能对原始的请求类Request类进行Wrap,然后我们自定义请求类继承这个Wrap的类。先贴代码,然后讲解。
package com.yong.volleyok.request;
/**
* Project: com.yong.volleyok
* Create Date: 2016/4/22
* Author: qingyong
* Description: 原始请求的包装
*/
public abstract class RequestWrapper<T> extends com.android.volley.Request<T> {
/**
* 请求
*/
protected HttpRequest mHttpRequest;
/**
* 结果
*/
protected HttpListener mHttpListener;
public RequestWrapper(HttpRequest httpRequest, HttpListener 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 params = getParams();
try {
for (Map.Entry 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 getHeaders() throws AuthFailureError {
return mHttpRequest.getHeaders();
}
/**
* 请求参数
*
* @return
* @throws AuthFailureError
*/
@Override
protected Map 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的最终的两个回调的方法:
@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:
package com.yong.volleyok.request;
/**
* Project: com.yong.volleyok.request
* Create Date: 2016/4/23
* Author: qingyong
* Description: Byte Request
*/
public class ByteRequest extends RequestWrapper<byte[]> {
public ByteRequest(HttpRequest httpRequest, HttpListener listener) {
super(httpRequest, listener);
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
}
}
可以看到经过我们的封装之后,实现变得非常简单了。
GsonRequest:
package com.yong.volleyok.request;
/**
* Project: com.yong.volleyok.request
* Create Date: 2016/4/23
* Author: qingyong
* Description: Gson Request
*/
public class GsonRequest<T> extends RequestWrapper<T> {
private static Gson mGson = new Gson();
private Class mClass;
private TypeToken mTypeToken;
public GsonRequest(Class tClass, HttpRequest httpRequest, HttpListener listener) {
this(tClass, null, httpRequest, listener);
}
public GsonRequest(Class tClass, TypeToken typeToken,
HttpRequest httpRequest, HttpListener listener) {
super(httpRequest, listener);
mClass = tClass;
mTypeToken = typeToken;
}
@SuppressWarnings("unchecked")
@Override
protected Response 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) 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种请求可以封装一个接口。
package com.yong.volleyok;
/**
* Project: com.yong.volleyok
* Create Date: 2016/4/23
* Author: qingyong
* Description: 请求
*/
public interface IHttpClient {
/**
* byte请求
*/
Request byteRequest(HttpRequest httpRequest, HttpListener<byte[]> listener, Object tag);
/**
* String请求
*/
Request stringRequest(HttpRequest httpRequest, HttpListener listener, Object tag);
/**
* gzip请求
*/
Request gZipRequest(HttpRequest httpRequest, HttpListener listener, Object tag);
/**
* JsonObject请求
* @return
*/
Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener listener, Object tag);
/**
* JsonArray请求
*/
Request jsonArrayRequest(String requestBody, HttpRequest httpRequest, HttpListener listener, Object tag);
/**
* Gson请求,可以映射Model
*/
Request gsonRequest(Class tClass, TypeToken typeToken, HttpRequest httpRequest, HttpListener listener, Object tag);
}
然后再写一个具体的实现类即可。
package com.yong.volleyok;
/**
* Project: com.yong.volleyok.http
* Create Date: 2016/4/23
* Author: qingyong
* Description: 请求的具体实现类
*/
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 listener) {
return stringRequest(httpRequest, listener, null);
}
@Override
public Request stringRequest(HttpRequest httpRequest, HttpListener listener, Object tag) {
StringRequest request = new StringRequest(httpRequest, listener);
addRequest(request, tag);
return request;
}
public Request gZipRequest(HttpRequest httpRequest, HttpListener listener) {
return gZipRequest(httpRequest, listener, null);
}
@Override
public Request gZipRequest(HttpRequest httpRequest, HttpListener listener, Object tag) {
GZipRequest request = new GZipRequest(httpRequest, listener);
addRequest(request, tag);
return request;
}
public Request jsonObjectRequest(HttpRequest httpRequest, HttpListener listener) {
return jsonObjectRequest(null, httpRequest, listener);
}
public Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener listener) {
return jsonObjectRequest(requestBody, httpRequest, listener, null);
}
@Override
public Request jsonObjectRequest(String requestBody, HttpRequest httpRequest, HttpListener listener, Object tag) {
JsonObjectRequest request = new JsonObjectRequest(requestBody, httpRequest, listener);
addRequest(request, tag);
return request;
}
public Request jsonArrayRequest(HttpRequest httpRequest, HttpListener listener) {
return jsonArrayRequest(httpRequest, listener, null);
}
public Request jsonArrayRequest(HttpRequest httpRequest, HttpListener listener, Object tag) {
return jsonArrayRequest(null, httpRequest, listener, tag);
}
@Override
public Request jsonArrayRequest(String requestBody, HttpRequest httpRequest, HttpListener listener, Object tag) {
JsonArrayRequest request = new JsonArrayRequest(requestBody, httpRequest, listener);
addRequest(request, tag);
return request;
}
public Request gsonRequest(Class tClass, HttpRequest httpRequest, HttpListener listener) {
return gsonRequest(tClass, httpRequest, listener, null);
}
public Request gsonRequest(Class tClass, HttpRequest httpRequest, HttpListener listener, Object tag) {
return gsonRequest(tClass, null, httpRequest, listener, tag);
}
@Override
public Request gsonRequest(Class tClass, TypeToken typeToken,
HttpRequest httpRequest, HttpListener listener, Object tag) {
GsonRequest request = new GsonRequest(tClass, typeToken, httpRequest, listener);
addRequest(request, tag);
return request;
}
}
六、使用
当然使用也非常简单,看HttpClient就知道有哪些方法。
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() {
@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