優雅設計封裝基於Okhttp3的網絡框架(六):HttpHeader接口設計實現 及 Response、Request封裝實現

到目前爲止,多線程下載功能設計、編寫、優化工作已經完成,但是網絡框架編寫工作並沒有完成,此篇將完成Http核心架構,編寫的新功能還是圍繞在http請求上,涉及到的知識點:

  • httpHeader的接口定義和實現
  • http請求頭和響應頭訪問編寫
  • http狀態碼定義
  • http中的 response封裝、request接口封裝和實現

(建議閱讀此篇文章之前,需理解前兩篇文章的講解,此係列文章是環環相扣,不可缺一,鏈接如下:)
優雅設計封裝基於Okhttp3的網絡框架(一):Http網絡協議與Okhttp3解析
優雅設計封裝基於Okhttp3的網絡框架(二):多線程下載功能原理設計 及 簡單實現
優雅設計封裝基於Okhttp3的網絡框架(三):多線程下載功能核心實現 及 線程池、隊列機制解析
優雅設計封裝基於Okhttp3的網絡框架(四):多線程下載添加數據庫支持(greenDao)及 進度更新
優雅設計封裝基於Okhttp3的網絡框架(五):多線程、單例模式優化 及 volatile、構建者模式使用解析


一. Http核心架構實現

下面將完成網絡框架核心架構,考慮到程序的擴展性,首要編寫的是接口,採用設計模式將相關的接口預留出來。

1. HttpHeader的接口定義和實現

(1)NameValueMap鍵值對接口

在定義接口之前,需要先定義一個鍵值對接口NameValueMap繼承Map接口,提供一些相關接口訪問,實現比較簡單,代碼如下:

public interface NameValueMap<K, V> extends Map<K, V> {

    String get(String name);

    void set(String name, String value);

    void setAll(Map<String, String> map);
}

(2)HttpHeader實現NameValueMap

HttpHeader是實現了整個Http請求的接口,該類在定義時實現NameValueMap接口後,需要實現很多方法。既然HttpHeader實現了鍵值對接口,所以在其內部需要維護一個private Map<String, String> mMap,此時可以根據此HashMap來完善待實現方法。

/**
 * @function Http請求、響應頭部字段訪問封裝
 * @author lemon Guo
 */

public class HttpHeader implements NameValueMap<String, String> {

    private Map<String, String> mMap = new HashMap<>();

    /*
    *   以下都是實現Map接口後需要實現的方法
    * */

    @Override
    public String get(String name) {
        return mMap.get(name);
    }

    @Override
    public void set(String name, String value) {
        mMap.put(name, value);
    }

    @Override
    public void setAll(Map<String, String> map) {
        mMap.putAll(map);
    }

    @Override
    public int size() {
        return mMap.size();
    }

    @Override
    public boolean isEmpty() {
        return mMap.isEmpty();
    }

    @Override
    public boolean containsKey(Object o) {
        return mMap.containsKey(o);
    }

    @Override
    public boolean containsValue(Object value) {
        return mMap.containsValue(value);
    }

    @Override
    public String get(Object o) {
        return mMap.get(o);
    }

    @Override
    public String put(String key, String value) {
        return mMap.put(key, value);
    }

    @Override
    public String remove(Object key) {
        return mMap.remove(key);
    }

    @Override
    public void putAll(Map<? extends String, ? extends String> map) {
        mMap.putAll(map);
    }

    @Override
    public void clear() {
        mMap.clear();
    }

    ......
}

2. Http請求頭和響應頭訪問編寫

下面就是對Http請求頭接口上的訪問和包裝,首先需要知道Http請求頭包含的內容,但是Http請求頭、響應頭所包含的內容是比較多的,這裏只在此封裝較爲常見的字段:

/**
 * @function Http請求、響應頭部字段訪問封裝
 * @author lemon Guo
 */
public class HttpHeader implements NameValueMap<String, String> {

    //常用的 Http字段

    public final static String ACCEPT = "Accept";
    public final static String PRAGMA = "Pragma";
    public final static String PROXY_CONNECTION = "Proxy-Connection";
    public final static String USER_AGENT = "User-Agent";
    public final static String ACCEPT_ENCODING = "accept-encoding";
    public final static String CACHE_CONTROL = "Cache-Control";
    public final static String CONTENT_ENCODING = "Content-Encoding";
    public final static String CONNECTION = "Connection";
    public final static String CONTENT_LENGTH = "Content-length";
    public static final String CONTENT_TYPE = "Content-Type";


    private Map<String, String> mMap = new HashMap<>();

    /*
    *   以上Http字段的get/set方法
    * */
    public String getAccept() {
        return get(ACCEPT);
    }

    public void setAccept(String value) {
        set(ACCEPT, value);
    }

    ......

    /*
    *   以下都是實現Map接口後需要實現的方法
    * */
    ......
}

以上已經封裝完成HttpHeader類——Http頭部相關字段的訪問。接下來還需要對Http請求方式進行一個簡單的封裝,除了常用的GET、POST方式還有其它5種,通過一個枚舉進行封裝即可。代碼如下:

/**
 * @function Http請求方式封裝
 * @author lemon Guo
 */

public enum HttpMethod {

    GET, POST, TRACE, PUT, DELETE, CONNECTION, OPTIONS
}

3. Http狀態碼定義

Http的狀態碼非常多,分佈於100~600,根據狀態碼也分成了幾種不同的類型,這裏也是封裝每種類型中較爲常見的,同樣採用枚舉方式。

  • HttpStatus類提供構造方法,參數爲狀態碼和含義。
  • 聲明狀態碼枚舉類型。
  • 對外提供方法isSuccess,通過判斷字節碼返回代表請求是否成功的boolean值
  • 對外提供方法getValue,通過傳入的參數狀態碼數字,返回狀態碼枚舉類型。
/**
 * @function Http狀態碼封裝
 * @author lemon Guo
 */

public enum HttpStatus {
    CONTINUE(100, "Continue"),
    SWITCHING_PROTOCOLS(101, "Switching Protocols"),

    OK(200, "OK"),
    CREATED(201, "Created"),
    Accepted(202, "Accepted "),
    NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
    NO_CONTENT(204, "No Content"),
    RESET_CONTENT(205, "Reset Content"),

    MULTIPLE_CHOICES(300, "Multiple Choices"),
    MOVED_PERMANENTLY(301, "Moved Permanently"),
    FOUND(302, "Found"),
    SEE_OTHER(303, "See Other"),
    USE_PROXY(305, "Use Proxy "),
    UNUSED(306, "Unused"),
    TEMPORARY_REDIRECT(307, "Temporary Redirect"),

    BAD_REQUEST(400, "Bad Request"),
    PAYMENT_REQUIRED(402, "Payment Required"),
    FORBIDDEN(403, "Forbidden"),
    NOT_FOUND(404, "Not_Found"),
    METHOD_NOT_ALLOWED(405, "Method Not Allowed "),
    NOT_ACCEPTABLE(406, "Not Acceptable"),
    REQUEST_TIMEOUT(408, "Request Timeout"),
    CONFLICT(409, "Conflict"),
    GONE(410, "Gone"),
    LENGTH_REQUIRED(411, "Length Required"),
    PAYLOAD_TOO_LARGE(413, "Payload Too Large"),
    URI_TOO_LONG(414, "URI Too Long"),
    UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type  Server Error"),
    FAILED(417, "Failed Server Error"),
    UPGRADE_REQUIRED(426, "Upgrade Required"),

    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
    NOT_IMPLEMENTED(501, "Not Implemented"),
    BAD_GATEWAY(502, "Bad_Gateway"),
    SERVICE_UNAVAILABLE(503, "Service Unavailable"),
    GATEWAY_TIMEOUT(504, "Gateway Timeout"),
    HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported ");

    private int mCode;

    private String mMessage;


    private HttpStatus(int code, String message) {
        this.mCode = code;
        this.mMessage = message;
    }

    public boolean isSuccess() {
        int value = mCode / 100;
        if (value == 2) {
            return true;
        }
        return false;
    }

    public static HttpStatus getValue(int value) {
        for (HttpStatus httpStatus : values()) {
            if (value == httpStatus.mCode) {
                return httpStatus;
            }
        }
        return null;
    }
}



二. Http中Response、Request封裝實現

在第一大點編碼中的類與接口實現比較基礎,難度不大,更像是實現Http核心框架前做的準備,所以說前期接口設計封裝直接決定整個程序的擴展性,需謹慎考慮全面。而接下來將對Http中的Response響應、Request請求進行封裝。

1. Response接口封裝

首先思考一下Response接口中需要封裝的方法,狀態碼和body的獲取必不可少,另外可以考慮狀態碼信息獲取、字節長度獲取方法。

(1)Header

在封裝Response之前還需要先封裝一個接口 —– Header,它是對所有響應頭、請求頭的封裝,而接口中只有一個getHeaders 方法,意味着響應頭、請求頭都會對其實現。代碼如下:

/**
 * @function 對所有響應頭、請求頭的封裝
 * @author lemonGuo
 */
public interface Header {
    HttpHeader getHeaders();
}

(2)HttpResponse接口

該接口繼承於Header、Closeable接口,組成爲:

  • 必須要有獲取狀態碼getStatus()方法、獲取Body輸入流getBody()方法、關閉輸入流close()方法。
  • 其次爲了考慮全面,新增了獲取狀態碼代表信息getStatusMsg()方法、獲取字節長度getContentLength()方法
/**
 * @funtion Http響應接口
 * @author nate
 */
public interface HttpResponse extends Header, Closeable {

    HttpStatus getStatus();
    String getStatusMsg();

    InputStream getBody() throws IOException;

    void close();
    long getContentLength();
}

(3)抽象類AbstractHttpResponse

以上HttpResponse 接口定義好之後,可以定義響應類實現,可是在此之前還需要定義一個抽象類AbstractHttpResponse,由它來實現HttpResponse 接口。

抽象類的好處

抽象類可以擁有自己的成員變量和已實現的方法,比接口的功能更加豐富。在封裝框架過程中,可藉由抽象類來實現一些內部的方法,更易擴展。

編碼實現

在此抽象類中處理響應數據時,需要多判斷一點:即是否爲壓縮數據,若是則對數據流進行處理後再作返回。該抽象類主要是對響應數據多做了一層判斷,相當於一個過濾網。

爲了處理壓縮這種情況,該抽象類實現了HttpResponse 接口中的getBody()close(),做了一些共性的預處理操作,同時爲具體實現的子類留出了getBodyInternal()closeInternal()抽象方法。

/**
 * @function AbstractHttpResponse 數據響應抽象類(繼承HttpResponse接口)
 * @author lemon guo
 */
public abstract class AbstractHttpResponse implements HttpResponse {

    private static final String GZIP = "gzip";

    private InputStream mGzipInputStream;

    @Override
    public void close()  {
        if (mGzipInputStream != null) {
            try {
                mGzipInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        closeInternal();
    }

    @Override
    public InputStream getBody() throws IOException {
        InputStream body = getBodyInternal();
        if (isGzip()) {
            return getBodyGzip(body);
        }
        return body;
    }

    protected abstract InputStream getBodyInternal() throws IOException;
    protected abstract void closeInternal();

    private InputStream getBodyGzip(InputStream body) throws IOException {
        if (this.mGzipInputStream == null) {
            this.mGzipInputStream = new GZIPInputStream(body);
        }
        return mGzipInputStream;
    }

    private boolean isGzip() {
        String contentEncoding = getHeaders().getContentEncoding();
        if (GZIP.equals(contentEncoding)) {
            return true;
        }
        return false;
    }
}

(4)實現類OkHttpResponse

回顧以上,已完成的接口、抽象類都是在爲Http響應實現類做準備,最後定義實現類OkHttpResponse,繼承抽象類AbstractHttpResponse,實現父類的方法:

  • 實現類內部定義兩個重要成員變量:響應類mResponse和Http字段訪問類mHeaders 。
  • 爲實現類提供構造方法,參數爲響應類Response。
  • 實現類內部待實現的方法具體編碼都依賴於以上兩個成員變量。

代碼量雖然不少,但是實現簡單,查看即可理解,代碼如下:

/**
 * @funtion: 實現類OkHttpResponse
 * @author lemon Guo
 */
public class OkHttpResponse extends AbstractHttpResponse {

    private Response mResponse;

    private HttpHeader mHeaders;

    public OkHttpResponse(Response response) {
        this.mResponse = response;
    }

    @Override
    protected InputStream getBodyInternal() {
        return mResponse.body().byteStream();
    }

    @Override
    protected void closeInternal() {
        mResponse.body().close();
    }

    @Override
    public HttpStatus getStatus() {
        return HttpStatus.getValue(mResponse.code());
    }

    @Override
    public String getStatusMsg() {
        return mResponse.message();
    }

    @Override
    public long getContentLength() {
        return mResponse.body().contentLength();
    }

    @Override
    public HttpHeader getHeaders() {
        if (mHeaders == null) {
            mHeaders = new HttpHeader();
        }

        for (String name : mResponse.headers().names()) {
            mHeaders.set(name, mResponse.headers().get(name));
        }
        return mHeaders;
    }
}


2. HttpRequest接口封裝和實現

接下來完成Http請求上的接口設計封裝,其實在真正瞭解以上Response的系列接口、抽象類、實現類設計實現後,會發現兩者的“套路”其實很相似。

(1)HttpRequest接口

首先來思考接口中的方法,一個請求很關鍵的是請求頭,然後是請求方式,最後是請求中傳遞的參數信息。

前期考慮封裝很重要,需設計全面,代碼實現如下:

/**
 * @funtion HttpRequest接口設計
 * @author nate
 */
public interface HttpRequest extends Header {

    HttpMethod getMethod();

    URI getUri();

    OutputStream getBody();

    HttpResponse execute() throws IOException;
}

(2)抽象類AbstractHttpRequest

如上,接口定義完之後,定義抽象類繼承該接口,實現了HttpResponse 接口中的getBody()getHeaders()execute()做一些共性的處理操作,同時爲具體實現的子類留出了executeInternal(HttpHeader mHeader)getBodyOutputStream()抽象方法。

同樣爲了考慮壓縮情況,在實現父類的方法中需要判斷當前請求過程中是否支持Zip壓縮,根據判斷結果來決定是否需要進一步處理輸出流。

請求、響應抽象類中的壓縮判斷代碼相同,都是爲了考慮壓縮情況,在接口的基礎上多做了一層封裝。編碼實現並不難,如下:

/**
 * @function AbstractHttpRequest 數據響應抽象類(繼承HttpRequest接口)
 * @author lemon guo
 */

public abstract class AbstractHttpRequest implements HttpRequest {

    private static final String GZIP = "gzip";

    private HttpHeader mHeader = new HttpHeader();

    private ZipOutputStream mZip;

    private boolean executed;

    @Override
    public HttpHeader getHeaders() {
        return mHeader;
    }

    @Override
    public OutputStream getBody() {
        OutputStream body = getBodyOutputStream();
        if (isGzip()) {

            return getGzipOutStream(body);
        }
        return body;
    }

    private OutputStream getGzipOutStream(OutputStream body) {
        if (this.mZip == null) {
            this.mZip = new ZipOutputStream(body);
        }
        return mZip;
    }

    private boolean isGzip() {

        String contentEncoding = getHeaders().getContentEncoding();
        if (GZIP.equals(contentEncoding)) {
            return true;
        }
        return false;
    }

    @Override
    public HttpResponse execute() throws IOException {
        if (mZip != null) {
            mZip.close();
        }
        HttpResponse response = executeInternal(mHeader);
        executed = true;
        return response;
    }

    protected abstract HttpResponse executeInternal(HttpHeader mHeader) throws IOException;

    protected abstract OutputStream getBodyOutputStream();
}

(3)請求流抽象處理BufferHttpRequest

到這裏你會發現接下來不是應該創建實現類麼?請求的封裝在此之前還需要再對抽象類進行一層封裝—–BufferHttpRequest,繼承於AbstractHttpRequest,在其中對請求流多做一步操作:

  • 內部維護一個成員變量:輸出字節流ByteArrayOutputStream
  • 在實現於父類的getBodyOutputStream方法中將成員變量輸出流返回出去。
  • 在實現於父類的executeInternal(HttpHeader header)方法中將成員變量內存數據轉換爲字節數組類型,即請求時傳遞參數的信息,再定義抽象方法將HttpHeader、字節數組兩個參數傳出。相當於在原理基礎上對輸出流多做了一步處理:轉換爲字節數組類型。

代碼實現如下:

/**
 * @funtion 請求流抽象處理BufferHttpRequest
 * @author lemon Guo
 */

public abstract class BufferHttpRequest extends AbstractHttpRequest {

    private ByteArrayOutputStream mByteArray = new ByteArrayOutputStream();

    protected OutputStream getBodyOutputStream() {
        return mByteArray;
    }

    protected HttpResponse executeInternal(HttpHeader header) throws IOException {
        byte[] data = mByteArray.toByteArray();
        return executeInternal(header, data);
    }

    protected abstract HttpResponse executeInternal(HttpHeader header, byte[] data) throws IOException;
}

(4)實現類OkHttpRequest

由於請求不同於響應的特殊性,需要考慮到頭部信息,在封裝兩次抽象類後,最後編寫實現類OkHttpRequest此類繼承於BufferHttpRequest ,具體實現爲:

  • 定義成員變量OkHttpClient及參數HttpMethodUrl來實現Okhttp的請求過程。
  • 提供構造方法初始化以上3個成員變量。
  • 實現抽象方法getMethod()getUri()。(這兩個抽象方法實現簡單,只需返回成員變量即可)
  • 實現抽象方法executeInternal(HttpHeader header, byte[] data)
    • 首先需要判斷請求方式是否爲POST,若是則意味着需要處理RequestBody,將客戶端傳遞的data參數封裝到RequestBody其中。
    • 創建請求Request.Builder,傳入URL、請求方式、RequestBody參數。
    • 接着對header進行處理,循環該參數將所有請求頭封裝至Request.Builder
    • 最後封裝完畢,調用成員變量OkHttpClient進行請求,獲取到響應數據Response
    • 創建上一點封裝好的響應實現類HttpResponse,將響應數據傳入其構造方法,最後將響應實現類HttpResponse返回出去即可。

此部分較爲重要,一定要將邏輯整理清楚,代碼實現如下:

/**
 * @function 實現類OkHttpRequest
 * @author lemon guo
 */

public class OkHttpRequest extends BufferHttpRequest {

    private OkHttpClient mClient;

    private HttpMethod mMethod;

    private String mUrl;

    public OkHttpRequest(OkHttpClient client, HttpMethod method, String url) {
        this.mClient = client;
        this.mMethod = method;
        this.mUrl = url;
    }

    @Override
    protected HttpResponse executeInternal(HttpHeader header, byte[] data) throws IOException {
        boolean isBody = mMethod == HttpMethod.POST;
        RequestBody requestBody = null;
        if (isBody) {
            requestBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), data);
        }
        Request.Builder builder = new Request.Builder().url(mUrl).method(mMethod.name(), requestBody);

        for (Map.Entry<String, String> entry : header.entrySet()) {
            builder.addHeader(entry.getKey(), entry.getValue());
        }
        Response response = mClient.newCall(builder.build()).execute();

        System.out.println("fuck "+response.body().contentLength());

        return new OkHttpResponse(response);
    }

    @Override
    public HttpMethod getMethod() {
        return mMethod;
    }

    @Override
    public URI getUri() {
        return URI.create(mUrl);
    }
}


3. 測試

在創建的module文件夾下會自動生成一個test文件夾,供開發人員測試所用,在這裏可以編寫一個簡單的網絡代碼來測試以上編碼工作是否正確,代碼如下:

public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);

        OkHttpClient client = new OkHttpClient();
        OkHttpRequest request = new OkHttpRequest(client, HttpMethod.GET, "http://www.baidu.com");

        HttpResponse response = request.execute();

        String content = null;
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody()));
        while ((content = bufferedReader.readLine()) != null){
            System.out.println(content);
        }
        response.close();
    }
}

結果顯示

這裏寫圖片描述

以上代碼邏輯爲GET方式請求百度,打印出請求到的資源數據,成功顯示,證明以上編碼無誤。




三. 工廠模式封裝HttpReques創建

此部分將對HttpRequest對象進行封裝,以上工作對於網絡框架而言是底層實現,那具體的網絡請求是調用OkhttpRequest來完成,但是此次封裝的網絡框架不僅支持Okhttp網絡框架請求方式,還支持原生UrlConnection請求,所以考慮到擴展性,完成原生請求,只需繼承HttpReques接口實現功能即可,但是對於上層業務調用,還是要對請求對象進行封裝。

通過一種機制來封裝HttpReques對象給上層調用,對於上層而言只需獲取HttpReques對象即可,至於該對象是通過Okhttp還是UrlConnection獲取並不關注。

1. 接口HttpRequestFactory

通過此接口的名字便知它內部採用的是工廠模式,而方法就是獲取HttpReques對象:

/**
 * @function 接口HttpRequestFactory(獲取HttpRequest對象)
 * @author lemon Guo
 */

public interface HttpRequestFactory {
    HttpRequest createHttpRequest(URI uri, HttpMethod method) throws IOException;
}

工廠設計模式

工廠設計模式可以解決的問題:

  • 不會向上層曝露對象創建的複雜過程,只提供結果。
  • 工廠模式是典型的解耦式設計,使職責單一化。

類圖

這裏寫圖片描述

結合此類圖至編寫的接口:

  • HttpRequestFactory就是工廠模式中的Creator
  • 接口中返回的HttpRequest 對象就是工廠模式中的Product
  • 下面就需要創建實現類繼承接口來“生產”Product,此實現類相當於工廠模式中的ConcreteCreator

2. 實現類

通過以上結合工廠模式的分析後,得知此類就是“生產”HttpRequest 對象的具體實現類,它繼承於HttpRequestFactory 接口,具體實現:

  • 定義成員變量OkhttpClient
  • 爲此類提供構造方法初始化成員變量
  • 實現接口中的createHttpRequest方法,即創建OkHttpRequest 對象並返回。
  • 再提供一些基本方法setConnectionTimeOut設置請求超時時間,setReadTimeOutsetWriteTimeOut設置讀寫時間。(若有其他需求,此處可繼續增加)
/**
 * @function 實現類OkHttpRequestFactory(返回HttpRequest對象)
 * @author lemon Guo
 */

public class OkHttpRequestFactory implements HttpRequestFactory {

    private OkHttpClient mClient;

    public OkHttpRequestFactory() {
        this.mClient = new OkHttpClient();
    }

    public OkHttpRequestFactory(OkHttpClient client) {
        this.mClient = client;
    }

    public void setReadTimeOut(int readTimeOut) {
        this.mClient = mClient.newBuilder().
                readTimeout(readTimeOut, TimeUnit.MILLISECONDS).
                build();
    }

    public void setWriteTimeOut(int writeTimeOut) {
        this.mClient = mClient.newBuilder().
                writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS).
                build();
    }

    public void setConnectionTimeOut(int connectionTimeOut) {
        this.mClient = mClient.newBuilder().
                connectTimeout(connectionTimeOut, TimeUnit.MILLISECONDS).
                build();
    }

    @Override
    public HttpRequest createHttpRequest(URI uri, HttpMethod method) {
        return new OkHttpRequest(mClient, method, uri.toString());
    }
}

3. 供上層調用HttpRequestProvider

接下來需要將OkHttpRequestFactory 的具體實現再做一次封裝供上層使用,創建一個類HttpRequestProvider

  • 定義成員變量HttpRequestFactory,用於創建HttpRequest對象。
  • 提供構造方法來初始化成員變量。注意這裏會判斷項目中是否使用Okhttp:
    • 若是,則創建OkHttpRequestFactory
    • 若不是,意味着請求需依賴原生UrlConnction,使用原生的Factory(後續會編寫)。
  • 提供方法getHttpRequest(URI uri, HttpMethod httpMethod),底層會調用HttpRequestFactory來創建HttpRequest對象。
/**
 * @function 封裝請求HttpRequestProvider,供上層調用
 * @author lemon Guo
 */

public class HttpRequestProvider {

    private static boolean OKHTTP_REQUEST = Utills.isExist("okhttp3.OkHttpClient", HttpRequestProvider.class.getClassLoader());

    private HttpRequestFactory mHttpRequestFactory;

    public HttpRequestProvider() {
        if (OKHTTP_REQUEST) {
            mHttpRequestFactory = new OkHttpRequestFactory();
        } else {
            mHttpRequestFactory = new OriginHttpRequestFactory();
        }
    }

    public HttpRequest getHttpRequest(URI uri, HttpMethod httpMethod) throws IOException {
        return mHttpRequestFactory.createHttpRequest(uri, httpMethod);
    }

    public HttpRequestFactory getHttpRequestFactory() {
        return mHttpRequestFactory;
    }

    public void setHttpRequestFactory(HttpRequestFactory httpRequestFactory) {
        mHttpRequestFactory = httpRequestFactory;
    }
}

最後,供給上層調用的就是HttpRequestProvider 類,它可以根據不同的條件創建不同Http請求library,這樣便實現多個library類庫切換的條件判斷使用。




四. 總結

1. 本篇總結

這裏寫圖片描述

以上截圖顯示着此篇博文完成的編碼內容,看起來編碼量很多,但是實現起來邏輯並不複雜。其中含有大量的接口、抽象類,這些都是在爲程序拓展性做準備,在下一篇博文中將添加新的類庫請求支持,你會發現在此基礎上只需增加3個類即可,充分體現出了程序的擴展性。

我們編寫的順序基本是 接口、枚舉 –> 抽象類 –>實現類,

  • 在第一點準備工作中完成了HttpHeader的設計實現、Http請求頭和響應頭訪問編寫、Http狀態碼封裝,這些並不複雜,只是最基本的封裝思想。
  • 在第二點中主要對Http中的RequestResponse進行設計封裝,這裏爲了程序擴展性邏輯稍顯複雜,先從接口開始定義,然後在此基礎上定義抽象類實現接口,在其內部進行共性操作,再留出抽象方法。
  • 第三點在現有的請求方式上再次封裝,採用工廠模式,提供最簡潔方法供上層調用。

此篇文章完成的部分還是有些多,需將邏輯捋清楚,但每個類實現並不難,重要的是它們之間的封裝關係,可對應以下代碼理解。

對應第六篇至以前所完成的源碼


2. 下篇預告

在下一篇博文中將添加新功能——原生請求的類庫支持,你會發現在此基礎上只需增加3個類即可,充分體現出了程序的擴展性。新增功能如下:

  • 原生HttpUrlConnction請求和響應
  • 業務層多線程分發處理
  • 移除請求
  • 請求成功類型轉換包裝處理


若有錯誤,虛心指教~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章