博客将按照下面的步骤介绍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); }
protected Map<String, String> getParams() throws AuthFailureError { return null; }会发现这样并不利于统一的调度,那其实在构造一个请求的时候,参数是不固定的,而且有的需要,有的不需要,这个时候,我们可以通过Builder模式来构造请求,可以进行如下的封装:
package com.yong.volleyok; /** * <b>Project:</b> com.yong.volleyok <br> * <b>Create Date:</b> 2016/4/22 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> 请求的封装,使用Builder模式进行构建 <br> */ 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的时候详细的说明。
其实就是回调的封装,在Volley里面是使用了两个接口来做的,这里统一成一个接口。
package com.yong.volleyok; /** * <b>Project:</b> com.yong.volleyok <br> * <b>Create Date:</b> 2016/4/22 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> 回调响应 <br> */ public interface HttpListener<T> { /** * 服务器响应成功 * * @param result 响应的理想数据。 */ void onSuccess(T result); /** * 网络交互过程中发生错误 * * @param error {@link VolleyError} */ void onError(VolleyError error); }
将成功和失败的回调封装到一个方法里面了。
为了以后能更好的升级的考虑,我们最好是不采用直接改源代码的方式,所以我们只能对原始的请求类Request类进行Wrap,然后我们自定义请求类继承这个Wrap的类。先贴代码,然后讲解。
package com.yong.volleyok.request; /** * <b>Project:</b> com.yong.volleyok <br> * <b>Create Date:</b> 2016/4/22 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> 原始请求的包装 <br> */ 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的最终的两个回调的方法:
@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; /** * <b>Project:</b> com.yong.volleyok.request <br> * <b>Create Date:</b> 2016/4/23 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> Byte Request <br> */ 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:
package com.yong.volleyok.request; /** * <b>Project:</b> com.yong.volleyok.request <br> * <b>Create Date:</b> 2016/4/23 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> Gson Request <br> */ 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种请求可以封装一个接口。
package com.yong.volleyok; /** * <b>Project:</b> com.yong.volleyok <br> * <b>Create Date:</b> 2016/4/23 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> 请求 <br> */ 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); }然后再写一个具体的实现类即可。
package com.yong.volleyok; /** * <b>Project:</b> com.yong.volleyok.http <br> * <b>Create Date:</b> 2016/4/23 <br> * <b>Author:</b> qingyong <br> * <b>Description:</b> 请求的具体实现类 <br> */ 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; } }六、使用
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地址: