Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现

$
0
0

到目前为止,多线程下载功能设计、编写、优化工作已经完成,但是网络框架编写工作并没有完成,此篇将完成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请求和响应
  • 业务层多线程分发处理
  • 移除请求
  • 请求成功类型转换包装处理


若有错误,虚心指教~

作者:ITermeng 发表于2017/8/2 10:31:57 原文链接
阅读:66 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>