优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
来源:互联网 发布:狄克斯特拉算法图解 编辑:程序博客网 时间:2024/05/19 01:10
到目前为止,多线程下载功能设计、编写、优化工作已经完成,但是网络框架编写工作并没有完成,此篇将完成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请求和响应
- 业务层多线程分发处理
- 移除请求
- 请求成功类型转换包装处理
若有错误,虚心指教~
- 优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
- 优雅设计封装基于Okhttp3的网络框架(二):多线程下载功能原理设计 及 简单实现
- 优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
- 优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制、终止线程解析
- 优雅设计封装基于Okhttp3的网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
- 优雅设计封装基于Okhttp3的网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换
- 优雅设计封装基于Okhttp3的网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
- 基于OkHttp3封装网络请求框架
- Retrofit+okhttp3的简单封装实现网络请求和拦截
- Java Develop——基于 okhttp3 的网络框架设计
- 一种封装多种实现的RPC设计
- 一种封装多种实现的RPC设计
- WebApi系列~基于单请求封装多请求的设计~请求的安全性设计与实现
- 基于AX88796B的网络接口模块的设计与实现
- 封装OkHttp3框架
- 接口封装设计
- 基于MVP架构的OKHttp3的封装
- 基于TMS320DM642的网络摄像机的设计及实现
- 顺序队列的基本操作
- nginx模块开发学习
- 最小生成树&最短路径
- 将ArcGIS属性表顺序变换保存方法
- VS2015安装QT插件
- 优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
- 解决手机连上笔记本wifi几分钟后就上不了网
- spark源码阅读二-spark-submit执行过程
- 汇编 LEA指令
- ARKit从入门到精通(1)-ARKit初体验
- APP的token登录机制详解
- Android 5.0 Camera系统源码分析(2):Camera打开流程
- 106/107/108 Convert * to Binary Search Tree 组转为二叉搜索树
- QT字符转换