本来想看看源码提高下自己的水平,以前也有看过某些框架的源码,但是基本都是只看某段,这次打算好好的研究一遍。本来是想拿RecyclerView的源码来开刀的,但是好像RecyclerView的代码没那么容易看懂,而且变量还贼多,还大量用了设计模式,讲真,看起来还真觉得挺费劲的。既然前段时间写了Http,那我就拿okhttp的代码来看看好了。
一.Okhttp
Android 4.4之后,HttpURLConnection底层实现已被OkHttp替换,okhttp内部依赖okio,现在的最新版本应该还是OkHttp3。其实我并直接的用过OkHttp来做网络请求的操作,我用的是Retrofit,基于OkHttp,其实差不多。
1.简单调用
一个简单的异步GET请求的话可以这样写
OkHttpClient mOkHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback());
2.okhttp的原理
我的总结就是获取到请求Call,进行一系列的Interceptors链的操作,异步的时候会涉及到Dispather的操作。
在网上很好的找到了一张图来说明这个过程(出自https://blog.piasy.com/2016/07/11/Understand-OkHttp/)
我觉得图中最核心的三个部分是
二.浅谈Okhttp执行流程
同步和异步在调用时的区别就是同步调用的是execute方法,异步调用的是enqueue方法。
1.OkHttpClient
这个类用了Builder模式
private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
}
里面也是有大量的属性,基本都是和整个请求过程中相关的参数,比如dns 啊,connectTimeout 啊这些,所以我把它看成是一个抽象整个请求过程的对象。而且它的注释也说了
就是一个Call的工厂,Call是什么之后会讲。
2.Request与Response
请求时会创建一个Request对象。这两个东西其实不用说就知道一个代表请求,一个代表响应。
看看这个Request其实也是在内部用了Builder模式
记住这个请求有五个属性,地址,方法(就是GET这些),tag表示的是标志,不知道是干啥的,好像是取消请求的时候会用到的。然后还有请求头和请求体。
(1)headers请求头
它有个参数,20个长度的列表,保存的是请求头相关的参数
比如addHeader方法,会调用Headers的add方法。
这个checkNameAndValue是判断key和value是否符合,先不用管。
然后看到addLenient中是先加name再加value,所以到这里你就能知道namesAndValues的数组是怎么存值的。
其实这些都不是很重要,简单了解一下就行。
(2)RequestBody请求体
它是一个抽象类,然后实体类,其实我这里主要是为了看这个,验证我上一篇讲http的请求体
可以看到请求的body是分普通情况和文件的情况。
这些都不重要。
3.Call
这个就是其中一个重要的东西了。
可以看到是用OkHttpClient的newCall方法创建Call,也验证了OkHttpClient就是Call的工厂。
Call是一次请求的抽象。既然是抽象,那就需要一个实现,那就是这里的RealCall,记住 它代表一次请求,它代表一次请求,它代表一次请求,这一听是没什么,但是它这样的一个思想我觉得是很好的,你想想一次请求是什么,是一个行为,一个过程,它这里把行为进行抽象,从而方便于进行管理与操作。
RealCall有4个属性
(1)它这里引用了OkHttpClient,我觉得是因为OkHttpClient里面保存了和整个过程相关的参数。但是这样相互引用的话耦合度会不会有点高。
(2)RetryAndFollowUpInterceptor是一个拦截器,后面会说。
(3)executed用来记录请求是否执行,一个请求只能执行一次
(4)originalRequest就是传进来的Request。
这个请求类,它的行为主要是一些请求的操作,比如开始同步请求,开始异步请求这些。所以如果你想对“请求”做什么操作,可以先来这个类来找找有没有对应的方法,比如取消请求,就在这里。
看看执行同步请求的方法。
第一个判断请求是否执行够,因为一个请求只允许执行一次。主要的操作是这3行
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
client.dispatcher().finished(this);
这里有涉及到client.dispatcher(),这是OkHttpClient的一个参数,看看是个啥子。Dispatcher,它又是一个okhttp里面比较重要的对象,表示检测者,负责监视整个请求的过程,它内部有很多的参数。
最大请求数maxRequests,最大主机请求数maxRequestsPerHost(好吧,其实我也不懂这是啥),有个Runnable,然后ExecutorService线程池,还有三个队列readyAsyncCalls、runningAsyncCalls和runningSyncCalls。
我讲讲这三个队列的意思,runningSyncCalls是存储同步请求,也就是Call(其实这里我有点蒙,你想想,同步请求如果有多个的话肯定是个排队请求的过程,而每个请求结束后都会移除队列,那这样的话这个队列不是一直都只有当前进行的请求吗,这样做有什么意义)。runningAsyncCalls是存储异步请求的队列,这个很有必要,因为异步是同时进行的,所以可以用个队列来存储,readyAsyncCalls表示准备进行异步请求的队列,因为有最大请的限制。
扯多了,看看同步请求时的executed方法和finished方法。
executed就是把请求添加到同步请求队列。
finish就是把请求从队列中移除。这个run()方法我不知道,我没找到在哪。
看完同步再来看看异步
异步的具体操作是写在Dispatcher里面
判断当前异步的队列数量做比较,超过了最大值就把Call添加到准备队列,否则添加到执行队列并执行线程,而AsyncCall继承了Runnable,所以executorService().execute(call)会执行call的run方法。这个run在内部又调用了execute方法,来看看这个方法。
@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) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
这个也不难看懂吧,主要两行代码
Response response = getResponseWithInterceptorChain();
client.dispatcher().finished(this);
finished和上面的一样讲过了,不同的是在里面多调用了个promoteCalls方法,其实就是对runningAsyncCalls、readyAsyncCalls两个队列做操作,也不难看懂,影响不大。
总结一下,不管是同步还是异步,做的操作都是 添加进队列——>调用getResponseWithInterceptorChain()方法——>从队列中移除。
到这里是不是就很容易看出getResponseWithInterceptorChain()就是请求网络整个过程的核心操作。
4.getResponseWithInterceptorChain()方法
我觉得这个就是整个okhttp里面最核心的操作,这个方法就是按顺序调用个个拦截器对请求进行处理。说得好听一点,这个叫责任链模式。
什么是责任链模式,不懂的我觉得有必要百度一波。简单点说就是一个请求就来,你对这个请求做处理或者不做处理,然后传给下一个人,一个一个的传。不了解的话不太容易看懂我下面的一些话。
getResponseWithInterceptorChain方法是RealCall类里面的,也可以说,这个请求执行了责任链的一系列操作。
interceptors表示存储拦截器的列表,进行添加一些列的拦截器操作之后,创建拦截器链对象,然后执行proceed方法就能返回请求的响应Response。
那么重心又到了RealInterceptorChain这个链对象的身上。
先要注意一下它的这些参数。
(1)interceptors是拦截器列表
(2)streamAllocation 一开始传空进来
(3)httpStream 一开始传空进来
(4)connection 一开始传空
(5)index表示当前链的一个下标。我上边也说了,责任链执行操作后丢给下一个执行,这里就是每次丢给下一个拦截器的时候index加一。所以初始肯定是0
(6)request就是请求,包含什么参数之前也有写。
简单了解一下参数后看看proceed,看看具体的请求数据的流程是咋样的。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpStream != null && !sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpStream != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
先这样,所有关于报错的判断我们先不管,抽出核心的代码。
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
它先创建一个新的责任链对象,然后下标加1,然后让第一个拦截器Interceptor 执行intercept操作,其实这个操作里面会调用新的责任链的proceed方法,这样就使得整个责任链模式给运作起来。其实我是感觉这个有点绕的,反正就是创建一个新的链对象,把链对象传给拦截器,然后拦截器里面又调用链对象的proceed方法,这个方法又重复这些操作直到最后一个拦截器。
稍微理清楚后来看看这个链上拦截器的一个顺序
其实就是列表中的顺序
先添加用户自定义的拦截器,这个我们后面再说吧。然后按这样的顺序添加
(1)RetryAndFollowUpInterceptor重试和重定向拦截器
(2)BridgeInterceptor 桥梁拦截器(我喜欢叫它配置请求拦截器)
(3)CacheInterceptor 缓存拦截器
(4)ConnectInterceptor 连接拦截器
(5)interceptors.addAll(client.networkInterceptors()); 是添加用户定义的网络请求拦截器(所以可以看出用户可以定义两种拦截器)
(7)CallServerInterceptor 内部的网络拦截器
因为我也并不是能完全的看懂这些拦截器内部的所有代码,所以我就不讲拦截器内详细做的操作了,免得误人子弟,我当时是看了这篇文章
https://blog.csdn.net/qq_19431333/article/details/53207220
其实还是看不懂详细的代码。