到目前为止,多线程下载功能设计、编写、优化工作已经完成,但是网络框架编写工作并没有完成,此篇将完成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及参数HttpMethod、Url来实现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
设置请求超时时间,setReadTimeOut
、setWriteTimeOut
设置读写时间。(若有其他需求,此处可继续增加)
/**
* @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中的Request、Response进行设计封装,这里为了程序扩展性逻辑稍显复杂,先从接口开始定义,然后在此基础上定义抽象类实现接口,在其内部进行共性操作,再留出抽象方法。
- 第三点在现有的请求方式上再次封装,采用工厂模式,提供最简洁方法供上层调用。
此篇文章完成的部分还是有些多,需将逻辑捋清楚,但每个类实现并不难,重要的是它们之间的封装关系,可对应以下代码理解。
2. 下篇预告
在下一篇博文中将添加新功能——原生请求的类库支持,你会发现在此基础上只需增加3个类即可,充分体现出了程序的扩展性。新增功能如下:
- 原生HttpUrlConnction请求和响应
- 业务层多线程分发处理
- 移除请求
- 请求成功类型转换包装处理
若有错误,虚心指教~