1. 程式人生 > >Android Volley+OkHttp3+Gson 開源庫的封裝

Android Volley+OkHttp3+Gson 開源庫的封裝

部落格將按照下面的步驟介紹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'
  • 1
  • 2
  • 3
  • 4

一、OkHttp3Stack的關於Volley的實現

這個是應該是比較簡單的,關於OkHttp3Stack的實現在github上面有實現,本部落格裡面的實現在我的Github上面。 
由於程式碼比較長,而且這個也不是這篇部落格的重點,大家需要的話可以去我的Github檢視。

二、HttpRequest的實現和HttpListener回撥監聽的封裝

2.1.HttpRequest的實現

通過檢視Volley的原始碼我們會發現,Volley的Request的構造方法是這樣寫的:

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

請求的引數有一些通過構造方法傳遞,而另外一些引數是通過Request裡面的很多的get方法得到的,比如post請求的時候的引數是通過Request類裡面的getParams()實現的,而我們需要做的就是如果需要post請求,那麼就重寫請求類,覆蓋裡面的getParams方法。

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

會發現這樣並不利於統一的排程,那其實在構造一個請求的時候,引數是不固定的,而且有的需要,有的不需要,這個時候,我們可以通過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);
        }

    }

}
  • 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
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

我們構造這樣的一個請求的類,然後在請求的時候可以通過這個類,去構建請求的時候需要的一些引數。這個類很簡單就不用詳細的講解了。具體的怎麼使用這個類去構造請求,我們會在Wrap Volley的Request的時候詳細的說明。

2.2.HttpListener的封裝

其實就是回撥的封裝,在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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

將成功和失敗的回撥封裝到一個方法裡面了。

三、Request的Wrap

為了以後能更好的升級的考慮,我們最好是不採用直接改原始碼的方式,所以我們只能對原始的請求類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);
        }
    }

}
  • 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

這裡最重要的就是這個類了,下面詳細的說一下,封裝的過程: 
我們在前面定義了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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這兩個方法一個是請求成功的方法,另外一個是請求失敗的方法,我們通過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));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

可以看到經過我們的封裝之後,實現變得非常簡單了。

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(respons