前言
OkHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso)
用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,现在已经打不出来),这是现在非常主流的一个网络请求框架了。
可能有人会说Retrofit+RxJava才是最主流的,好吧,其实Retrofit的强大也是基于OkHttp,其是在OkHttp的基础上进一步封装,所以OkHttp也是切入Retrofit源码学习的入口。
博主对Retrofit2.x和OkHttp3.0也是比较熟悉的,刚好最近比较有空,接着周末时间总结了OkHttp。
本系列将带领大家从源码的出发,做到尽可能详细地剖析OkHttp的每一个知识点。
该系列的第一篇文章中我们已经了解了OkHttp从发起一个请求到收到网络回调数据的流程(Request——>Response)的过程。
本文的主要是从源码出发,带大家了解从发起一个Request请求到调用Dispatcher(分发器)调用线程池,来实现同/异步发起请求数据的流程,及内部涉及的设计模式和原理。
本系列文章:
OkHttp源码彻底解析(一)OkHttp请求流程
OkHttp源码彻底解析(二)OkHttp架构及API源码
OkHttp源码彻底解析(三)OkHttp3.0拦截器原理——责任链模式
OkHttp源码彻底解析(四)OkHttp拦截器的作用
OkHttp源码彻底解析(五)OkHttp连接池
目录
前言
客户端请求的数据Request、服务端返回的数据Response——Builder模式
Request:
Response:
okHttpClient——外观模式,组合模式
newCall
call
dispatcher与executorService
执行请求
Converter工厂模式
OkHttp整体流程
这是OkHttp请求的流程,也是本章讲解的流程,图中省略处为拦截器部分(责任链模式)本章涉及,将在下一篇博客提到
我们都知道,要是有网络请求的API之前,必须先有请求的信息,也就是request
客户端请求的数据Request、服务端返回的数据Response——Builder模式
首先要明白,Requset与Response为什么使用Builder模式
因为它们需要的参数满足这两点中的一点:
1.参数多且杂 ; 2.参数不是必须要传入的
值得注意的一点是:数据请求类 的 url 是必须传的,会在 build() 方法里检查,如果 为空会报异常
Request:
这是一个 请求数据 的封装类(封装了请求头、请求地址等等)
public final class Request {
//url字符串和端口号信息,默认端口号:http为80,https为443.其他自定义信息
private final HttpUrl url;
//"get","post","head","delete","put"....
private final String method;
//包含了请求的头部信息,name和value对。最后的形势为:$name1+":"+$value1+"\n"+ $name2+":"+$value2+$name3+":"+$value3...
private final Headers headers;
//请求的数据内容
private final RequestBody body;
//请求的附加字段。对资源文件的一种摘要。保存在头部信息中:ETag: "5694c7ef-24dc"。客户端可以在二次请求的时候,在requst的头部添加缓存的tag信息(如If-None-Match:"5694c7ef-24dc"),服务端用改信息来判断数据是否发生变化。
private final Object tag;
//各种附值函数和Builder类
...
}
其中,ResponseBody是请求的具体内容,是抽象类
public abstract class RequestBody {
...
//返回内容类型
public abstract MediaType contentType();
//返回内容长度
public long contentLength() throws IOException {
return -1;
}
//如何写入缓冲区。BufferedSink是第三方库okio对输入输出API的一个封装,不做详解。
public abstract void writeTo(BufferedSink sink) throws IOException;
}
OKHttp3中给出了两个requestBody的实现FormBody 和 MultipartBody,分别对应了两种不同的MIME类型:"application/x-www-form-urlencoded"和"multipart/"+xxx.作为的默认实现
其中,有一个重要的抽象方法writeTo
public abstract void writeTo(BufferedSink sink) throws IOException;
该方法的参数BufferedSink是Okio的封装,就是一个sink就是从本地写出的特殊的IO流。
这个抽象方法会在最后一个拦截器CallServerInterceptor里面,也就是最终发起网络请求的部分被调用
request.body().writeTo(bufferedRequestBody);
把request装换成bufferedRequestBody,并作为IO流通过Socket写到目标网络中,当然,在这一步之前还有好多好多好多操作,这里简单先提一下。
Response:
public final class Response implements Closeable {
//网络请求的信息
private final Request request;
//网路协议,OkHttp3支持"http/1.0","http/1.1","h2"和"spdy/3.1"
private final Protocol protocol;
//返回状态码,包括404(Not found),200(OK),504(Gateway timeout)...
private final int code;
//状态信息,与状态码对应
private final String message;
//TLS(传输层安全协议)的握手信息(包含协议版本,密码套件(https://en.wikipedia.org/wiki/Cipher_suite),证书列表
private final Handshake handshake;
//相应的头信息,格式与请求的头信息相同。
private final Headers headers;
//数据内容在ResponseBody中
private final ResponseBody body;
//网络返回的原声数据(如果未使用网络,则为null)
private final Response networkResponse;
//从cache中读取的网络原生数据
private final Response cacheResponse;
//网络重定向后的,存储的上一次网络请求返回的数据。
private final Response priorResponse;
//发起请求的时间轴
private final long sentRequestAtMillis;
//收到返回数据时的时间轴
private final long receivedResponseAtMillis;
//缓存控制指令,由服务端返回数据的中的Header信息指定,或者客户端发器请求的Header信息指定。key:"Cache-Control"
//详见<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC 2616,14.9</a>
private volatile CacheControl cacheControl; // Lazily initialized.
//各种附值函数和Builder类型 ...
}
其中,上面比较重要的是:1.ResponseBody是获取的数据内容,2.三个Response:网络返回的、从cache中读取的、重定向后保存的之前的网络请求返回数据,3.Requset网络请求信息,4.Headers响应头:可以知道缓存指令,5.code状态码:404就是错误,6.CacheControl 缓存控制指令,由服务器返回的Header或客户端Header指定
介绍了Request和Response之后,我们来了解如何通过这个Requset来得到Pesronse,已经这里面API的内部逻辑
首先,OKHttp3在项目中发起网络请求的API如下:
okHttpClient.newCall(request).execute();
我们按顺序来了解这个API涉及的类的源码:
okHttpClient——外观模式,组合模式
OkHttp是一个比较庞大的网络请求框架(Retrofit内部也是使用OkHttp这个框架),为了方便地和这个框架内部复杂的子模块进行交互,OkHttpClient使用了外观模式来实现。将OKHttp的很多功能模块,全部包装进这个类中,让这个类单独提供对外的API,这种设计叫做外观模式。将操作都隐藏起来,减少用户的交互成本。
由于内部功能模块太多,使用了Builder模式(生成器模式)来构造。
它的方法只有一个:newCall.返回一个Call对象(一个准备好了的可以执行和取消的请求)。
newCall
先来看源码:
@Override
public Call newCall(Request request) {
return new RealCall(this, request);
}
我们可以看到,newCall其实是返回一个RealCall类,也就是说我们的同/异步请求网络数据,实际上都是调用这个RealCall的execute/enqueue方法。这是一个Call接口的实现类
public interface Call {
Request request();
//同步的方法,直接返回Response
Response execute() throws IOException;
//异步的,传入回调CallBack即可(接口,提供onFailure和onResponse方法)
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
interface Factory {
Call newCall(Request request);
}
}
Call接口提供了内部接口Factory(用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置,工厂方法模式)。
dispatcher与executorService
dispatcher与executorService分别是分发器与线程池,承接上面的RealCall,
OKHttpClient类中有个成员变量dispatcher负责请求的分发。既在真正的请求RealCall的execute方法中,使用dispatcher来执行任务:
RealCall的execute方法:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
//使用dispatcher 来分发任务
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
RealCall的enqueue方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//使用dispatcher来将人物加入队列
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
OKHttp3中分发器只有一个类 ——Dispathcer.
也就是说,enqueue/execute(同/异步)都是内部调用了dispatcher来执行任务,
同步操作:dispathcer调用自己的execute方法
流程:
1.在RealCall中client.dispatcher().execute(this); 其中this就是RealCall
2.executorService()就是获取一个线程池
3.RealCall的execute内部是executorService().execute(this);线程池executorService调用他的execute(call) ( call也就是上面的new AsyncCall(responseCallback))
异步操作:dispathcer调用自己的enqueue方法
流程:
1.在RealCall中client.dispatcher().enqueue(new AsyncCall(responseCallback));其中,AsyncCall与上面同步时的RealCall形对比,同步调用RealCall,异步调用RealCall的内部类AsyncCall的enqueue
2.executorService()方法获取一个线程池,而
3.AsyncCall的enqueue内部是executorService().execute(call);线程池executorService调用他的execute(call) ( call也就是上面的new AsyncCall(responseCallback))
下面我们可以通过源码来看看Dispatcher
线程池executorService:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
参数:
0:核心线程数量。保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
Integer.MAX_VALUE: 线程池可容纳线程数量。
60,TimeUnit.SECONDS: 当线程池中的线程数大于核心线程数时,空闲的线程会等待60s后才会终止。如果小于,则会立刻停止。
new SynchronousQueue<Runnable>():线程的等待队列。同步队列,按序排队,先来先服务。
Util.threadFactory("OkHttp Dispatcher", false): 线程工厂,直接创建一个名为 “OkHttp Dispathcer”的非守护线程。
(2) 执行同步的Call:直接加入runningSyncCalls队列中,实际上并没有执行该Call,交给外部执行。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
(3) 将Call加入队列:如果当前正在执行的call数量大于maxRequests,64,或者该call的Host上的call超过maxRequestsPerHost,5,则加入readyAsyncCalls排队等待。否则加入runningAsyncCalls,并执行。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
(4) 从ready到running的轮转,在每个call 结束的时候调用finished,并:
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//每次remove完后,执行promoteCalls来轮转。
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
//线程池为空时,执行回调
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
(5) 线程轮转:遍历readyAsyncCalls,将其中的calls添加到runningAysncCalls,直到后者满。
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return;
}
}
执行请求
同步的请求RealCall 实现了Call接口:
可以execute,enqueue和cancle。
异步的请求AsyncCall(RealCall的内部类)实现了Runnable接口:
只能run(调用了自定义函数execute).
execute 对比:
RealCall:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
//分发。实际上只是假如了队列,并没有执行
client.dispatcher().executed(this);
//实际上的执行。
Response result = getResponseWithInterceptorChain();
//返回结果
if (result == null) throw new IOException("Canceled");
return result;
} finally {
//执行完毕,finish
client.dispatcher().finished(this);
}
}
AsyncCall:
@Override protected void execute() {
boolean signalledCallback = false;
try {
//实际执行。
Response response = getResponseWithInterceptorChain();
//执行回调
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//执行完毕,finish
client.dispatcher().finished(this);
}
}
实际上的执行函数都是getResponseWithInterceptorChain():
private Response getResponseWithInterceptorChain() throws IOException {
//创建一个拦截器列表
List<Interceptor> interceptors = new ArrayList<>();
//优先处理自定义拦截器
interceptors.addAll(client.interceptors());
//失败重连拦截器
interceptors.add(retryAndFollowUpInterceptor);
//接口桥接拦截器(同时处理cookie逻辑)
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//分配连接拦截器
interceptors.add(new ConnectInterceptor(client));
//web的socket连接的网络配置拦截器
if (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
//最后是连接服务器发起真正的网络请求的拦截器
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//流式执行并返回response
return chain.proceed(originalRequest);
}
补充一下,对于初学者而言,同步请求可能存在误区,误以为同步请求时主线程是阻塞的,这是错误的,无论同步或者异步都是在线程池里面进行的,都是不阻塞主线程的。这里关于同步异步的区别在于,同步请求synchronized关键字锁住了整个代码,那么如果当前OkhttpClient已经执行了一个同步任务,如果这个任务没有释放锁,那么新发起的请求将被阻塞,直到当前任务释放锁,而异步请求可以同时发起多个请求,平行执行。所以同步异步的概念应该是存在于执行任务的线程池中的各个子线程,而不是主线程与子线程。
这里额外提一下converter
Converter工厂模式
converter:序列化,反序列化的工具(对应Requset和Response),实现数据类型的转换,例Gson解析等
converterFactory是converter的工厂模式,用来构建各种converter,
可以添加converterFactory由retrofit完成requestBody和responseBody的构造。
这里对retrofit2不展开讨论,后续会出新的文章来详细讨论。仅仅介绍一下converterFacotry,以及它是如何构建OkHttp3中的RequestBody和ResponseBody的。
Note: retrofit2中的Response与okhttp3中的response不同,前者是包含了后者。既retrofit2中的response是一层封装,内部才是真正的okhttp3种的response。
我们项目中的一个converterFacotry代码如下:
public class RsaGsonConverterFactory extends Converter.Factory {
//省略部分代码
...
private final Gson gson;
private RsaGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
//将返回的response的Type,注释,和retrofit的传进来,返回response的转换器。Gson只需要type就可以将responseBody转换为需要的类型。
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new RsaGsonResponseBodyConverter<>(gson, adapter);
}
//将request的参数类型,参数注释,方法注释和retrofit传进来,返回request的转换器。Gson只需要type就可以将request对象转换为OKHttp3的reqeustBody类型。
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new RsaGsonRequestBodyConverter<>(gson, adapter);
}
}
该Factory(工厂方法模式,用于动态的创建对象)主要是用来生产response的converter和request的converter。显然我们使用了Gson作为数据转换的桥梁。分别对应如下两个类:
response的converter(之所以命名为Rsa,是做了一层加解密):
public class RsaGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
RsaGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
直接将value中的值封装为JsonReader供Gson的TypeAdapter读取,获取转换后的对象。
request的converter:
final class RsaGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
RsaGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
//如果是RsaReq的子类,则进行一层加密。
if(value instanceof RsaReq){
//加密过程
}
//不需要加密,则直接读取byte值,用来创建requestBody
else {
//这个构造方法是okhttp专门为okio服务的构造方法。
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
}
上面的流操作使用的是第三方库okio。可以看到,retrofit,okhttp,okio这三个库是完全相互兼容并互相提供了专有的API。
好了,这里就是本章内容,本章介绍了OkHttp的架构,API内部的源码,我们可以看到里面包含了许多值得学习的设计模式,
Requset和Response的builder模式
converter的工厂模式
OkHttpClient的外观模式,组合模式
————————————————
版权声明:本文为CSDN博主「Kyrie_Wangyz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39037047/article/details/84674653
网友评论