Android网络请求框架—OKHttp 源码解析
Posted Gjson
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android网络请求框架—OKHttp 源码解析相关的知识,希望对你有一定的参考价值。
总体流程整个流程是,通过OkHttpClient
将构建的Request
转换为Call,然后在RealCall中进行异步或同步任务,最后通过一些的拦截器interceptor
发出网络请求和得到返回的response
。
将流程大概是这么个流程,大家可以有个大概的印象,继续向下看:
OkHttp流程图.jpg
为了让大家有更深的印象,我准备追踪一个GET
网络请求的具体流程,来介绍在源码中发生了什么。
GET请求过程
这是利用OkHttp
写一个Get请求步骤,这里是一个同步的请求,异步的下面也会说:
//HTTP GET
public String get(String url) throws IOException
//新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
//新建一个Request对象
Request request = new Request.Builder()
.url(url)
.build();
//Response为OKHttp中的响应
Response response = client.newCall(request).execute();
if (response.isSuccessful())
return response.body().string();
else
throw new IOException("Unexpected code " + response);
OKHttpClient:流程的总控制者
OkHttpClient的类设计图
使用OkHttp的时候我们都会创建一个OkHttpClient对象:
OkHttpClient client = new OkHttpClient();
这是做什么的呢?看下builder里面的参数:
final Dispatcher dispatcher; //分发器
final Proxy proxy; //代理
final List<Protocol> protocols; //协议
final List<ConnectionSpec> connectionSpecs; //传输层版本和连接协议
final List<Interceptor> interceptors; //拦截器
final List<Interceptor> 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; // 验证确认响应证书 适用 HTTPS 请求连接的主机名。
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 超时</Interceptor></Interceptor></ConnectionSpec></Protocol>
在这些声明的对象中可以看出来,几乎所有用到的类都和OkHttpClient
有关系。事实上,你能够通过它来设置改变一些参数,因为他是通过建造者模式
实现的,因此你可以通过builder()
来设置。如果不进行设置,在Builder
中就会使用默认的设置:
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
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;
看到这,如果你还不明白的话,也没关系,在OkHttp
中只是设置用的的各个东西。真正的流程要从里面的newCall()
方法中说起:
/**
* Prepares the @code request to be executed at some point in the future.
* 准备将要被执行的request
*/
@Override
public Call newCall(Request request)
return new RealCall(this, request);
当通过建造者模式
创建了Request
之后(这个没什么好说),紧接着就通过下面的代码来获得Response
大家还记得上面做GET
请求时的这句代码吧:
Response response = client.newCall(request).execute();
这就代码就开启了整个GET请求的流程:
RealCall:真正的请求执行者。
先看一下他的构造方法:
protected RealCall(OkHttpClient client, Request originalRequest)
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
可以看到他传过来一个OkHttpClient
对象和一个originalRequest
(我们创建的Request
)。
接下来看它的execute()
方法:
@Override
public Response execute() throws IOException
synchronized (this)
if (executed) throw new IllegalStateException("Already Executed"); //(1)
executed = true;
try
client.dispatcher.executed(this);//(2)
Response result = getResponseWithInterceptorChain();//(3)
if (result == null) throw new IOException("Canceled");
return result;
finally
client.dispatcher.finished(this);//(4)
- 检查这个
call
是否已经被执行了,每个call
只能被执行一次,如果想要一个完全一样的call
,可以利用all#clone
方法进行克隆。 - 利用
client.dispatcher().executed(this)
来进行实际执行,dispatcher
是刚才看到的OkHttpClient.Builder
的成员之一,它的文档说自己是异步HTTP
请求的执行策略,现在看来,同步请求它也有掺和。 - 调用
getResponseWithInterceptorChain()
函数获取HTTP
返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作。 -
最后还要通知
dispatcher
自己已经执行完毕。
dispatcher
这里我们不过度关注,在同步执行的流程中,涉及到dispatcher
的内容只不过是告知它我们的执行状态,比如开始执行了(调用executed
),比如执行完毕了(调用finished
),在异步执行流程中它会有更多的参与。
真正发出网络请求,解析返回结果的,还是getResponseWithInterceptorChain
:
//拦截器的责任链。 private Response getResponseWithInterceptorChain() throws IOException // Build a full stack of interceptors. List<Interceptor> 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 (!retryAndFollowUpInterceptor.isForWebSocket()) interceptors.addAll(client.networkInterceptors()); //(6) interceptors.add(new CallServerInterceptor( retryAndFollowUpInterceptor.isForWebSocket())); //(7) Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); // <<=========开始链式调用 <="" code=""/></Interceptor>
- 在配置
OkHttpClient
时设置的interceptors
; - 负责失败重试以及重定向的
RetryAndFollowUpInterceptor
; - 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的
BridgeInterceptor
; - 负责读取缓存直接返回、更新缓存的
CacheInterceptor
; - 负责和服务器建立连接的
ConnectInterceptor
; - 配置
OkHttpClient
时设置的networkInterceptors
; - 负责向服务器发送请求数据、从服务器读取响应数据的
CallServerInterceptor
。 - 在
return chain.proceed(originalRequest);
中开启链式调用:
RealInterceptorChain
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
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.
//如果我们已经有一个stream。确定即将到来的request会使用它
if (this.httpCodec != 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().
//如果我们已经有一个stream, 确定chain.proceed()唯一的call
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);
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");
return response;
`
代码很多,但是主要是进行一些判断,主要的代码在这:
// Call the next interceptor in the chain.
//调用链的下一个拦截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request); //(1)
Interceptor interceptor = interceptors.get(index); //(2)
Response response = interceptor.intercept(next); //(3)
- 实例化下一个拦截器对应的
RealIterceptorChain
对象,这个对象会在传递给当前的拦截器 - 得到当前的拦截器:
interceptors
是存放拦截器的ArryList
- 调用当前拦截器的
intercept()
方法,并将下一个拦截器的RealIterceptorChain
对象传递下去
除了在client中自己设置的interceptor
,第一个调用的就是retryAndFollowUpInterceptor
RetryAndFollowUpInterceptor:负责失败重试以及重定向
直接上代码
@Override
public Response intercept(Chain chain) throws IOException
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()));
int followUpCount = 0;
Response priorResponse = null;
while (true)
if (canceled)
streamAllocation.release();
throw new IOException("Canceled");
Response response = null;
boolean releaseConnection = true;
try
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); //(1)
releaseConnection = false;
catch (RouteException e)
// The attempt to connect via a route failed. The request will not have been sent.
//通过路线连接失败,请求将不会再发送
if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
releaseConnection = false;
continue;
catch (IOException e)
// An attempt to communicate with a server failed. The request may have been sent.
// 与服务器尝试通信失败,请求不会再发送。
if (!recover(e, false, 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.
// 附加上先前存在的response。这样的response从来没有body
// TODO: 2016/8/23 这里没赋值,岂不是一直为空?
if (priorResponse != null) // (2)
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
Request followUp = followUpRequest(response); //判断状态码 (3)
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)
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()));
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);
中直接调用了下一个拦截器,然后捕获可能的异常来进行操作 - 这里没看太懂,有点坑,以后补
- 这里对于返回的response的状态码进行判断,然后进行处理
BridgeInterceptor:
负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 。
@Override
public Response intercept(Chain chain) throws IOException
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//检查request。将用户的request转换为发送到server的请求
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.
//GZIP压缩
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == 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()); //(3)
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse))
GzipSource responseBody = new以上是关于Android网络请求框架—OKHttp 源码解析的主要内容,如果未能解决你的问题,请参考以下文章