編輯:關於Android編程
博客將按照下面的步驟介紹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 extends com.android.volley.Request {
/**
* 請求
*/
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 {
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 extends RequestWrapper {
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 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 listener) {
return byteRequest(httpRequest, listener, null);
}
@Override
public Request byteRequest(HttpRequest httpRequest, HttpListener 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
下面是效果圖,看看是不是親想要的效果圖,如果是,這段代碼你就可以參考下了,但是要靈活運用,根據需求做相應的改動。<LinearLayout xmlns:androi
期待已久的MIUI 8終於上線啦!經過全新設計的MIUI 8靈感來源於變幻萬千的“萬花筒”,在色彩、交互動畫、系統字體等方面的大膽改
我們生活中使用微信,如果看到的好東西,如小視頻,圖片,小段子等等,就會收藏起來,那麼微信收藏怎麼導出到電腦呢?不清楚的朋友可以來看看微信收藏導出到電腦方法流
隨著移動互聯網的快速發展,它已經和我們的生活息息相關了,在公交地鐵裡面都能看到很多人的人低頭看著自己的手機屏幕,從此“低頭族”一詞就產生了,作為一