从去年开始,项目里面就一直使用grpc来处理网络请求,由于是新接触的,网络上并没有造好的轮子,我就简单的用MVC模式封装了下(想看的可以:https://www.jianshu.com/p/d6ede6e219f0)。由于封装的很简单粗暴,于是就想参考下其他网络请求框架,然后去完善。刚好以前的老同事都说okhttp用起来很爽,之前也用过okhttp,我这爆脾气,我忍它很久了,上去就是扒源码,一层一层扒的那种。
言归正传,我们知道okhttp分同步跟异步的实现,我们先来看看okhttp的同步使用:
//同步调用
1 OkHttpClient okHttpClient = new OkHttpClient();
2 Request request = new Request.Builder().url("http://......").build();
3 try {
4 Response response = okHttpClient.newCall(request).execute();
5 //。。。
6 } catch (IOException e) {
7 e.printStackTrace();
8 }
look,上面就这么几行代码,就完成了一次同步调用,我们先来看下第一行代码,创建了一个OkHttpClient对象,我们来看看它的构造:
public OkHttpClient() {
this(new Builder());
}
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.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
原来如此,当用new OkHttpClient()这种方式创建OkHttpClient对象,会当场创建一个Builder对象,并给OkHttpClient内部的一些成员变量使用默认值。那我们再去看看Builder的构造:
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
......还有N多代码
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
......还有N多代码
}
Builder内部有两个构造,刚才new OkHttpClient()内部使用的是 this(new Builder()),默认当你使用这种方式创建OkHttpClient对象时候,同时Builder通过无参构造函数也创建了builder对象。回到OkHttpClient ,this(new Builder())此处调用了OkHttpClient(Builder builder) {}这个构造并且在此处对成员变量进行赋值。
在这里面,通过搞了个Builder静态内部类,估计是为了方便给OkHttpClient成员变量赋值,这么多成员变量不封装个对象来传递,那当赋值的时候,方法里面得加多少形参,想想都可怕。
很好,那我如果要改变内部成员变量的赋值呢,第二种创建OkHttpClient方式出来了,请看下面:
//生成OkHttpClient对象的另外一种方式
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//设置链接超时时间5秒
builder.connectTimeout(5, TimeUnit.SECONDS);
......此处省略N行
builder.build();
很好,第一行代码就是创建OkHttpClient对象,并且给他内部的成员变量赋值,什么超时时间,拦截器等等之类的为后面请求数据做准备。我们来看看第2行代码,关于Request对象的生成,一开始创建了Request静态内部类Builder对象,下面的.url("..")其实就是给bulider对象里面的成员变量赋值,然后通过.build()方法把builder类当做参数创建一个Request对象,看下面代码:
//这是要分析的第2行代码
Request request = new Request.Builder().url("http://......").build();
//.build()方法
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
对于Request,也是通过静态内部类Builder给成员变量赋值,我们看看它的构造,刚好上面的.build()方法调用了:
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
发起网络请求一般都需要一个url跟若干个参数(method,body等),Request就是包含这些参数的载体。
目前为止发起请求的前奏已经搞完了,是时候进行请求并获取数据,我们来分析下第4行代码:
Response response = okHttpClient.newCall(request).execute();
我们先肢解下上面的代码,Response, newCall。前者一看就知道是网络请求返回的数据,我们来看看newCall(request)方法,上一步分析的request作为该方法的参数,我们点进去瞧瞧:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
以上我们可以知道newCall()方法的返回值是一个call,看下面源码,其实这个call就是一个接口,RealCall是call的一个实现类,在分析RealCall之前,我们先来看看call接口的定义:
public interface Call extends Cloneable {
/** Returns the original request that initiated this call. */
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
...还有其他定义
}
call内部只是定义了一些方法,具体的实现在RealCall类里面,还记得第四行的okHttpClient.newCall(request).execute(), 这个execute()在上面定义有,返回的是Response对象,毫无疑问这里面是进行网络请求的实现。
很好,newCall()方法内部,调用了RealCall的newRealCall()方法并且返回了RealCall对象,我们来瞅瞅newRealCall()方法的内部实现:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
方法内部就三行代码,第一行new了个RealCall对象call,第二行给call 增加了一个eventListener监听,只是对一个Call的状态监听这里我们不care它,继续扒:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
这。。尼玛全是给RealCall赋值,把之前创建的okhttpClient对象,Request对象都赋值给了RealCall相对应的成员变量。现在我们回到execute()方法,上面是分析了创建RealCall对象,我们接下来来看看execute()。还记得第4行代码吗(我绝对不是拿来凑字数的):
Response response = okHttpClient.newCall(request).execute();
okHttpClient.newCall(request)创建的其实就是call的实现类RealCall的实例,接下来,激动人心的时刻到了,具体的实现就在RealCall里面的execute()方法里面:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
方法开头就来个同步锁,锁的是当前RealCall对象,锁的好处大家都懂吧,避免线程不安全的现象发生,这里面就是防止对executed字段的值读写不一致。executed = true的时候,标明正在执行,并抛出Already Executed异常。 client.dispatcher().executed(this)这句其实就是把当前RealCall对象放到一个队列看下面:
//RealCall.java
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//Dispatcher.java
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
最终获取Resopnse对象的是这一行: Response result = getResponseWithInterceptorChain(),我们来看看getResponseWithInterceptorChain这个方法,我加了些备注:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
//自定义的拦截器
interceptors.addAll(client.interceptors());
//请求出错重试拦截
interceptors.add(retryAndFollowUpInterceptor);
//为请求(request before)添加请求头,为响应(Response Before)添加响应头的拦截
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//对缓存的处理
interceptors.add(new CacheInterceptor(client.internalCache()));
//连接相关的拦截器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这个方法里面,interceptors列表,request请求体,还有之前okhttpClient对象的部分成员变量,都作为参数new了一个RealInterceptorChain对象,而且这个方法的返回竟然是chain.proceed(originalRequest),我们直接来看看proceed()方法,里面只是贴出了关键代码:
//RealInterceptorChain.java
public final class RealInterceptorChain implements Interceptor.Chain {
......
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
......
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
......
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
......
return response;
}
}
这里面很巧妙的把所有的interceptor都执行了个遍,一开始index默认是0,每次获取一个interceptor,执行index + 1,可以看出,是有顺序的遍历interceptors集合,看上面代码:Response response = interceptor.intercept(next),关键点就在这里,假装我们没有自定义interceptor,那一开始调用的拦截器就是retryAndFollowUpInterceptor,我们来看看它的intercept()方法:
//RetryAndFollowUpInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
......
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
......
}
}
看到了吧,这个方法里面调用了realChain.proceed(.....)(对于所有拦截器,都存在这样的一句代码),然后又会执行index + 1,直到完全完全结束。由于拦截器太多,提一下关键的ConnectInterceptor,客户端和服务端建立连接部分:
public final class ConnectInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
核心代码:HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks),这里面实现的是获取一个可用的链接:
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
......
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
......
}
上面代码中,关键在于findHealthyConnection(),内部又调用了findConnection()方法,很长很长。。。这里不分析,后期专门每个拦截器去研究。
上面获取到了连接,真正获取到response数据,是在CallServerInterceptor里面,这是排在最后的一个拦截器,我们再来看看CallServerInterceptor的interceptor()方法:
@Override public Response intercept(Chain chain) throws IOException {
......
//流分配,StreamAllocation会通过ConnectPool获取或者新生成一个RealConnection来得到一个连接到Server的Connection连接
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
......
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
......
}
可怕,handshake(握手,socket不是经常扯到握手吗)都出来了,对于我们来说的那熟悉的httpURLconnection怎么没有出现。。我找了下资料,原来OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程,OkHttp和HttpUrlConnection是同一级的,用socket实现了网络连接。
篇幅过长,异步的下一篇在分析了。