对于网络请求,一直都是在用一些优秀的开源框架如Vollery,okhttp,Retrofit等,虽然项目中一般不会让自己手写网络请求框架,但是我们在使用中不仅要会使用,还要“知其所以然”,特别现在面试还需要你至少看过一个项目的源码,正常开发中我们也应该多读别人的代码,来提高自己,
一、集成与使用
1、集成
Ecipse:下载jar包,放入libs文件夹下,
AndroidStudio:直接再当前项目的gradle内引入:
compile 'com.squareup.okhttp:okhttp:2.4.0'
2、使用
2.1、同步请求
2.1.1、get请求
//创建okhttpclient对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建一个请求
Request request = new Request.Builder()
.url("http://xxxx")
.build();
//执行网络请求,并返回数据
Response response=okHttpClient.newCall(request).execute();
String data = response.body().string();
解释:首先构建okhttpClient对象,然后创建请求体,然后通过okHttpClient构建Call并执行execute()方法,返回数据,
注意:请求回的数据response会包含很多信息,例如head,body,内容在body内,然后调用string()方法,而不是toString()方法获取,
2.1.2、post请求
//创建okhttpclient对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建body
RequestBody formBody = new FormEncodingBuilder()
.add("","")
.build();
//通过url,body构建一个请求
Request request = new Request.Builder()
.url("http://www.xxxxxx")
.post(formBody)
.build();
//执行网络请求,并返回数据
Response response=okHttpClient.newCall(request).execute();
String data = response.body().string();
解释:post再官方文档中可以添加很多格式如JSON、文件、流等,此处的body构建方法是常用的简单参数请求方法,想了解更多可以参考此博客:http://blog.csdn.net/mynameishuangshuai/article/details/51303446,此处就不多分析了。
2.2、异步
//创建okhttpclient对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建一个请求
Request request = new Request.Builder()
.url("http://xxxx")
.build();
//创建一个call
Call call =okHttpClient.newCall(request);
//进行网络请求
call.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) { //请求失败
Log.e("--json--"," shibaile ");
}
@Override
public void onResponse(Response response) throws IOException { //请求成功
try {
//获取数据,此线程为子线程,想要操作主线程需要使用runOnUiThread()方法
String jsondata =response.body().string();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
解释:异步与同步相比是因为调用了enqueue()方法实现了call.back,数据会通过back里的回调方法onResponse()传递过来。
二、源码分析
思路:
1、要分析源码,我们理所当然的要从头开始,再这里我们的头是什么?是okhttpClient,我们需要去okhttpClient类中看构造方法都做什么了记录下来,
2、然后看Request,这个请求体都包含什么内容,定义了什么,再往下,
3、然后就是newcall方法了,我们再去看okhttpClient的newcall方法内部调用那些方法再往下看,
4、最终我们再看call的enqueue()(异步调用)和execute()(同步调用)方法到底怎样讲我们的请求与服务器交互的,又是怎样实现了callback来实现数据的回传,基本大致就是这个思路,接下来我们分析:
**1、okhttpClient的构建:
直接看okhttpClient的构建方法:源码如下:**
//内部构造器
public OkHttpClient() {
this(new Builder()); //调用下面的Builder()方法
}
接下来看new Builder()内都做了什么,Builder是OkHttpClient的内部类,如下:
final Dispatcher dispatcher; //分发器
final Proxy proxy; //代理
final List protocols; //协议
final List connectionSpecs; //传输层版本和连接协议
final List interceptors; //拦截器
final List networkInterceptors; //网络拦截器
final ProxySelector proxySelector; //代理选择
final CookieJar cookieJar; //cookie
final Cache cache; //缓存
final InternalCache internalCache; //内部缓存
final SocketFactory socketFactory; //socket 工厂
final SSLSocketFactory sslSocketFactory; //安全套接层socket 工厂,用于HTTPS
final CertificateChainCleaner certificateChainCleaner;//验证确认响应证书
final HostnameVerifier hostnameVerifier; // 主机名字确认
final CertificatePinner certificatePinner; // 证书链
final Authenticator proxyAuthenticator; //代理身份验证
final Authenticator authenticator; // 本地身份验证
final ConnectionPool connectionPool; //连接池,复用连接
final Dns dns; //域名
final boolean followSslRedirects; //安全套接层重定向
final boolean followRedirects; //本地重定向
final boolean retryOnConnectionFailure; //重试连接失败
final int connectTimeout; //连接超时时长
final int readTimeout; //read 超时
final int writeTimeout; //write 超时
这里main定义了很多如connectTimeout(连接超时时长),dns(域名)等再构建OkHttpClient时可以通过builde添加,如果不添加则去builde内部则默认定义这些参数:
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
2、来看Request的内容,request是通过其builder创建的,看下面:
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
public HttpUrl url() {
return url;
}
public String method() {
return method;
}
public Headers headers() {
return headers;
}
public String header(String name) {
return headers.get(name);
}
public List headers(String name) {
return headers.values(name);
}
public @Nullable RequestBody body() {
return body;
}
public Object tag() {
return tag;
}
...
解释:由上面源码可知我们构建Request时可以添加请求的url、设置get或者post请求的method 、body、tag等,类似一个实体类的bean,用来存储这些信息,
3、根据上面的第三部我们来看下newcall方法内部执行了什么:
newcall是由okhttpClient调用的,那我们去这里找一下,看源码:
/**
* 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 */);
}
我们可以看到这里返回的是RealCall,我们接下来去找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;
}
再看这里调用的RealCall 的构造方法,如下:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client; // @1
this.originalRequest = originalRequest; // @2
this.forWebSocket = forWebSocket; // @3
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // @4
}
解释:从上面可以看到
3.1、this.client 就是我们构建的OkHttpClient
3.2、this.originalRequest是我们上面构建包含url、method等的Request;
3.3,forWebSocket 是一个boolean类型的数据表示:
The application's original request unadulterated by redirects or auth headers
//应用程序的原始请求不掺杂重定向或身份验证头,就是表示是否胃最开始的原始请求
3.4、我们可以卡他的源码声明表示这是一个:从失败中恢复,并根据需要跟踪重定向的拦截器,用于重定向的
4、接下来我们需要分析call的enqueue()(异步调用)和execute()(同步调用)具体是怎么实现与服务器通信的,由于上面知道我们这里的call是RealCall,所以我们去RealCall内部去找enqueue()和execute()方法:
4.1、execute()—–同步调用
看源码:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed"); // @1
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this); // @2
Response result = getResponseWithInterceptorChain(); // @3
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this); // 4
}
}
由上面我们可以看到:
1、判断当前的call是否被执行过,一个call只能执行一次,如果想要一个完全一样的 call,可以利用 all#clone 方法进行克隆。
2、利用 client.dispatcher().executed(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一,它的文档说自己是异步 HTTP 请求的执行策略,现在看来,同步请求它也有掺和。
3、最终的Response 是由getResponseWithInterceptorChain()函数获取 HTTP 返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作。
4、最后还要通知 dispatcher 自己已经执行完毕
其实最重要的还是看respone是怎么来的,所以我们来看getResponseWithInterceptorChain():
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // @1
interceptors.add(retryAndFollowUpInterceptor); // @2
interceptors.add(new BridgeInterceptor(client.cookieJar())); // @3
interceptors.add(new CacheInterceptor(client.internalCache())); // @4
interceptors.add(new ConnectInterceptor(client)); // @5
if (!forWebSocket) { //上面定义的是否胃原始请求
interceptors.addAll(client.networkInterceptors()); // @6
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // @7
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest); // 8
}
由上面可知:
1、在配置 OkHttpClient 时设置的 interceptors;
2、负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3、负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4、负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5、负责和服务器建立连接的 ConnectInterceptor;
6、配置 OkHttpClient 时设置的 networkInterceptors;
7、负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
8、开始链式调用
重点:整个网络请求这里最重要了,再上面我们知道interceptors 集合添加了各种参数,但是最终返回数据的是 8 位置,所以我们先看 8 这个位置是怎么样讲这些参数调用的:
4.1.1 RealInterceptorChain.proceed源码如下:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//index由其构造器中传入,我们可以看到是0,当interceptors长度小于等于0时,抛出“声明错误”异常
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
//如果我们已经有了一个流,请确认传入的请求将使用它。
if (this.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
//确认下一个拦截器对链..继续()进行了必要的调用。
if (httpCodec != 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");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
//将respone返回
return response;
}
还是看根本找respone是怎么来的,
// Call the next interceptor in the chain.
//在链中调用下一个拦截器。
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout); // @1
Interceptor interceptor = interceptors.get(index); // @2
Response response = interceptor.intercept(next); // @3
解释:
1、与我们上面getResponseWithInterceptorChain()内调用的方法一样,重新实例化下一个拦截器对应的RealIterceptorChain对象,
2、获取当前的interceptor,由上面知道interceptors是一个装载interceptor的List集合;
3、调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对象传递下。
有上面的getResponseWithInterceptorChain()方法中我们知道她分贝添加的是
interceptors----retryAndFollowUpInterceptor---BridgeInterceptor==CacheInterceptor--ConnectInterceptor--networkInterceptors--CallServerInterceptor,
我们刚刚说过respone是当前拦截器调用intercept方法并将下一个拦截器的RealIterceptorChain对象传递下去,下面分析retryAndFollowUpInterceptor;(都说上面的方法是责任链模式,因为对此不精通所以没有说明,以后研究之后改正)
4.1.2 retryAndFollowUpInterceptor –重定向拦截器 ,我们看他重写的方法intercept:
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
//他试图通过一条线路连接失败。请求将不会被发送。
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
//试图与服务器通信的尝试失败了。请求可能已经发送了。
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
//我们抛出了一个未检查的异常。释放任何资源。
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
可以看出在response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);中直接调用了下一个拦截器,然后捕获可能的异常来进行操作,然后我们看下一个拦截器中的proceed
4.1.3 BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应,我们看它重写的方法intercept():
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body(); // @1
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build()); // @2
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) { // @3
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
解释:
1、1到2位置都是校验我们的请求Request,并且通过requestBuilder重新构建了一个能更规范的request,
2、调用下一个拦截器返回数据;
3、对返回的数据进行处理
接下来看下一个拦截器
4.1.4 CacheInterceptor 负责读取缓存直接返回、更新缓存的 ,看源码:
@Override
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null // @1
? cache.get(chain.request()) //通过request得到缓存
: null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //根据request来得到缓存策略 @2
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) { //存在缓存的response,但是不允许缓存
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 缓存不适合,关闭
}
// If we're forbidden from using the network and the cache is insufficient, fail.
//如果我们禁止使用网络,且缓存为null,失败
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_BODY)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// If we don't need the network, we're done.
//没有网络请求,跳过网络,返回缓存
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);//网络请求拦截器 // @3
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
//如果我们因为I/O或其他原因崩溃,不要泄漏缓存体
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get. @4
//如果我们有一个缓存的response,然后我们正在做一个条件GET
if (cacheResponse != null) {
if (validate(cacheResponse, networkResponse)) { //比较确定缓存response可用
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
//更新缓存,在剥离content-Encoding之前
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (HttpHeaders.hasBody(response)) { // =========(5)
CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
response = cacheWritingResponse(cacheRequest, response);
}
return response;
}
1、三元运算符判断是否有缓存,有的话直接通过缓存的request来获取respone,然后进行判断当前response是否有效,没有将cacheCandate赋值为空。
2、根据request判断缓存的策略,是否要使用了网络,缓存 或两者都使用
3、调用下一个拦截器,与服务器交互得到response
4、如果本地已经存在cacheResponse,那么让它和网络得到的networkResponse做比较,决定是否来更新缓存的cacheResponse
5、缓存未经缓存过的response
当没有缓存的时候我们需要去请求网络,接下来看如何请求建立链接的
4.1.5 ConnectInterceptor 建立连接
@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.
//我们需要网络来满足这个请求。可能是为了验证条件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 对象,它将在后面的步骤中被使用,那它又是何方神圣呢?它是对 HTTP 协议操作的抽象,有两个实现:Http1Codec和Http2Codec,顾名思义,它们分别对应 HTTP/1.1 和 HTTP/2 版本的实现。
在Http1Codec中,它利用 Okio 对Socket的读写操作进行封装,Okio 以后有机会再进行分析,现在让我们对它们保持一个简单地认识:它对java.io和java.nio进行了封装,让我们更便捷高效的进行 IO 操作。
而创建HttpCodec对象的过程涉及到StreamAllocation、RealConnection,代码较长,这里就不展开,这个过程概括来说,就是找到一个可用的RealConnection,再利用RealConnection的输入输出(BufferedSource和BufferedSink)创建HttpCodec对象,供后续步骤使用。(还在研究这里)
4.1.6 NetworkInterceptors 配置OkHttpClient时设置的 NetworkInterceptors。就是正常的配置,
4.1.7 CallServerInterceptor 发送和接收数据
@Override public Response intercept(Chain chain) throws IOException {
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { // @1
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
httpCodec.finishRequest();
Response response = httpCodec.readResponseHeaders() // @2
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
解释:
1、检查请求方法,用Httpcodec处理request
2、进行网络请求得到response,最终返回respone
同步请求总结:前面说了拦截器用了责任链设计模式,它将请求一层一层向下传,知道有一层能够得到Resposne就停止向下传递,然后将response向上面的拦截器传递,然后各个拦截器会对respone进行一些处理,最后会传到RealCall类中通过execute来得到esponse。
4.2异步请求
上面我们看的是call的同步,接下来看call的enqueue()(异步调用)
//异步调用的方法
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
看最好一行代码,调用了Dispatcher类中的enqueue(Call ),继续看:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
解释:如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,同时利用线程池执行call;否者将call加入到readyAsyncCalls中。runningAsyncCalls和readyAsyncCalls是什么呢?看下面:
/** Ready async calls in the order they'll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>(); //正在准备中的异步请求队列
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>(); //运行中的异步请求
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>(); //同步请求
call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类:
//异步请求
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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);
}
}
}
AysncCall中的execute()中的方法,同样是通过Response response = getResponseWithInterceptorChain();来获得response,这样异步任务也同样通过了interceptor,剩下的流程就和上面一样了。
五、整体流程图(正在绘制。。。)
六、总结:本文就okhttp的使用及源码进行分析,但是分析过程是由调用的方法去源码中查找,然后一点一点的拨开,感觉还是欠缺思路,在此特备感谢我参考两篇文章,给我指引了方向,如有侵权还请告知,接下还会继续阅读该源码,因为有的地方还么有吃透,再此提醒各位,看源码时不要太纠结于代码如何实现,“只见树木,不见森林”,要自己跳出来,希望本文对有需要得人能有所帮助,
参考博客:https://blog.piasy.com/2016/07/11/Understand-OkHttp/ 源码分析
参考博客:http://www.jianshu.com/p/27c1554b7fee 更透彻
如有侵权还请告知,立即改正!感谢生活、感谢科技、感谢分享!