1、使用方式
在不使用网络请求框架的情况下,我们通常需要经过以下步骤完成一次网络请求:

[图片上传中...(image.png-279b4-1619598133702-0)]
1、首先build request参数
2、因为不能在主线程请求HTTP,所以你得有个Executer或者线程enqueue后,通过线程去run你的请求
4、得到服务器数据后,callback回调给你的上层
在这个过程中需要做好线程切换,解析读取数据,然后切回主线程,回调给上层,这些过程会有非常多的坑 因此才会有很多的网络开源框架被使用到,下面总结OkHttp 网络框架 的使用方法及原理。
//构建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new LoggerInterceptor())
.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.build();
//构建Request
Request getRequest = new Request.Builder()
.addHeader("name","android")
.url("www.baidu.com")
.get() //get 请求
.build();
//创建post 请求
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
String requestBody = "I am white7"; //提交内容为String
//也可在外部直接创建RequestBody
RequestBody requestBodyOut = new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return MediaType.parse("text/x-markdown; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("I am Jdqm.");
}
};
Request postRequest = new Request.Builder()
.addHeader("name","white7")
.url("www.baidu.com")
.post(RequestBody.create(mediaType,requestBody))
.build();
//构建Call
Call call = okHttpClient.newCall(postRequest);
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
//同步请求
try {
new Thread(new Runnable() {
@Override
public void run() {
Response execute = call.execute();
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
可以看到发起一次请求需要OkhttpClient、 Request、 Call 三个对象参与。先看OkHttpClient
2、OkHttpClient
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher; //调度器
this.proxy = builder.proxy; //代理配置
this.protocols = builder.protocols; //支持协议配置
this.interceptors = Util.immutableList(builder.interceptors); //请求拦截器
this.networkInterceptors = Util.immutableList(builder.networkInterceptors); //网络请求拦截器
this.eventListenerFactory = builder.eventListenerFactory; //流程监听器
this.proxySelector = builder.proxySelector; /代理选择器
this.cookieJar = builder.cookieJar;//Cookie策略,是否保存Cookie
this.cache = builder.cache; //缓存配置
this.socketFactory = builder.socketFactory;//socket配置
boolean isTLS = false;
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory; //https socket配置
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager); //https socket配置
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
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; //Dns配置
this.followSslRedirects = builder.followSslRedirects; /是否可以从HTTP重定向到HTTPS
this.followRedirects = builder.followRedirects; //是否重定向
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout; //请求超时配置 0代表不会超时
this.connectTimeout = builder.connectTimeout; //写入超时
this.readTimeout = builder.readTimeout; //写入超时
this.writeTimeout = builder.writeTimeout; //写入超时
this.pingInterval = builder.pingInterval; /针对HTTP2和web socket的ping间隔
、、、
}
}
可以看到 OkHttpClient 本质是一个集合了多个 请求配置的 配置类,并且提供了 Builder 模式的设计,使用者可以灵活配置。
3、Request
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);
}
Request类用来描述请求的参数信息等,包含域名、请求方式、请求头、请求体等一系列信息。和Okhttpclient 一样Request 类也是一个配置类。
4、Call
Call 是一个接口,具体的实现是RealCall
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.timeout = new AsyncTimeout() {
@Override protected void timedOut() {
cancel();
}
};
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
可以看到Call 中关联了 client 和Request。 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方法 创建AsyncCall 对象作为参数并注入responseCallback。
}
5、Dispatcher 的enqueue 方法分析
从Call 的 enqueue 方法可以得出,最终的请求在Dispatcher 内部:
//准备队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//运行队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
...
void enqueue(call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
Dispatcher 类中有两个队列,分别保存 就绪的AsyncCall 和运行的AsyncCall , 在添加完 AsyncCall 后会调用promoteAndExecute 方法。
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { //从就绪队列中 取出符合规则的 AsyncCall 调用
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // 正在运行的此call 是否大于 maxRequest
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // 相同端口的运行 call 数量是否大于maxRequestsPerHost
i.remove(); //从就绪队列中移除符合规则的 AsyncCall
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall); //添加到运行队列中
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService()); //调用AsyncCall 的executeOn 方法 并存入一个线程池
}
return isRunning;
}
6、RealCall 的 executeOn 方法
...
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this); //使用线程执行 AsyncCall
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); //从运行队列中移除 AsyncCall
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain(); //调用getResponseWithInterceptorChain 方法获取Response
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true; //失败回调
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true; //成功回调
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
...
}
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //手动添加的拦截器
interceptors.add(retryAndFollowUpInterceptor); //处理错误和重定向
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);
}
RealCall 的enqueue 最后通过getResponseWithInterceptorChain 方法获取 Response, 从该方法内部可以看到,其通过注入 连接器 集合 & Request 对象 & RealCall & eventListener & 读写超时 以及index == 0 的参数 实例化了一个 RealInterceptorChain, 通过该对象的proceed 方法获取的响应结果。话不多说,进去看看-->
RealInterceptorChain 类
...
@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 {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
...
// 创建新的 RealInterceptorChain 该方法首次创建的streamAllocation、streamAllocation、 httpCodec 都为空
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//获取 index 位置的 拦截器 如果用户有添加自定义的 拦截器 那么先取得是 自定义的拦截器
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next); //
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");
}
return response;
}
在没有添加自定义拦截器,按照以下顺序进行拦截的器调用。

6.1 RetryAndFollowUpInterceptor
这个拦截器用来进行错误重试和重定向
@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 streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
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); //重新调用chain 的proceed
releaseConnection = false;
} catch (RouteException e) {
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp;
try {
followUp = followUpRequest(response, streamAllocation.route());
} catch (IOException e) {
streamAllocation.release();
throw e;
}
if (followUp == null) {
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);
this.streamAllocation = streamAllocation;
} 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;
}
}
在RetryAndFollowUpInterceptor 拦截器中只是重新调用了 realChain.proceed ,不同的是这里新增了一个 StreamAllocation 对象进去,然后交给BridgeInterceptor 处理。
6.2 BridgeInterceptor
这个拦截器是应用和网络交互的一个桥梁。他会根据请求内容在Request中添加一些请求头,这些都是用户未感知到的。同时这个拦截器还会读取Cookie配置,如果有Cookie信息,也会通过请求头带到服务端。
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
//对请求体数据添加到 head中
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()); //在对请求头数据设置完成后交给下一个拦截器
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)) {
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();
}
在完成请求头的配置后,交给CacheInterceptor 拦截器,这里主要处理缓存流程,后面单独将缓存。在缓存拦截器后面是ConnectInterceptor 拦截器:
6.3 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.
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 和RealConnection 对象,又调用了realChain.proceed ,可见realChain 的功能随着拦截链的调用越来越强了。
6.4 CallServerInterceptor
这是所有拦截器中的最后一个拦截器。在这个拦截器里会进行IO操作与服务器交互。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
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();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}