闲来无事阅读了一下OkHttp的源码,发现OkHttp根本就不基于HttpURLConnection或者HttpClient
OkHttp是基于java Socket自己实现了一套java层http协议,当然还支持SPDY,https,http2.0等。
用OkHttp,一个请求怎么去写呢
OkHttpClient client = new OkHttpClient();
Request request =new Request.Builder().url(apiRequest.requestUrl).post(apiRequest.requestParams.getRequestBody()).build();
try {
Response response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
或者
Request request = new Request.Builder().url(url).post(requestBody).build();
client.newCall(request).enqueue(callback);
简单来说呢,就是传入RequestBody和url就可以发起请求,同步和异步两种方式。
先看OkHttpClient的构造,首先是静态初始化
static {
Internal.instance = new Internal() {
@Override public Transport newTransport(
Connection connection, HttpEngine httpEngine) throws IOException {
return connection.newTransport(httpEngine);
}
@Override public boolean clearOwner(Connection connection) {
return connection.clearOwner();
}
@Override public void closeIfOwnedBy(Connection connection, Object owner) throws IOException {
connection.closeIfOwnedBy(owner);
}
@Override public int recycleCount(Connection connection) {
return connection.recycleCount();
}
@Override public void setProtocol(Connection connection, Protocol protocol) {
connection.setProtocol(protocol);
}
@Override public void setOwner(Connection connection, HttpEngine httpEngine) {
connection.setOwner(httpEngine);
}
@Override public boolean isReadable(Connection pooled) {
return pooled.isReadable();
}
@Override public void addLenient(Headers.Builder builder, String line) {
builder.addLenient(line);
}
@Override public void addLenient(Headers.Builder builder, String name, String value) {
builder.addLenient(name, value);
}
@Override public void setCache(OkHttpClient client, InternalCache internalCache) {
client.setInternalCache(internalCache);
}
@Override public InternalCache internalCache(OkHttpClient client) {
return client.internalCache();
}
@Override public void recycle(ConnectionPool pool, Connection connection) {
pool.recycle(connection);
}
@Override public RouteDatabase routeDatabase(OkHttpClient client) {
return client.routeDatabase();
}
@Override public Network network(OkHttpClient client) {
return client.network;
}
@Override public void setNetwork(OkHttpClient client, Network network) {
client.network = network;
}
@Override public void connectAndSetOwner(OkHttpClient client, Connection connection,
HttpEngine owner, Request request) throws RouteException {
connection.connectAndSetOwner(client, owner, request);
}
@Override
public void callEnqueue(Call call, Callback responseCallback, boolean forWebSocket) {
call.enqueue(responseCallback, forWebSocket);
}
@Override public void callEngineReleaseConnection(Call call) throws IOException {
call.engine.releaseConnection();
}
@Override public Connection callEngineGetConnection(Call call) {
return call.engine.getConnection();
}
@Override public BufferedSource connectionRawSource(Connection connection) {
return connection.rawSource();
}
@Override public BufferedSink connectionRawSink(Connection connection) {
return connection.rawSink();
}
@Override public void connectionSetOwner(Connection connection, Object owner) {
connection.setOwner(owner);
}
@Override
public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) {
tlsConfiguration.apply(sslSocket, isFallback);
}
};
}
其实就是实现了一个Internal类并初始化一个实例,全局通用。
而真正的构造函数里就两行
public OkHttpClient() {
routeDatabase = new RouteDatabase();
dispatcher = new Dispatcher();
}
没有什么好解释的,后面会着重去看Dispatcher
然后我们构造Request对象,用client.newCall.execute或者enqueue,去执行。
protected Call(OkHttpClient client, Request originalRequest) {
// Copy the client. Otherwise changes (socket factory, redirect policy,
// etc.) may incorrectly be reflected in the request when it is executed.
this.client = client.copyWithDefaults();
this.originalRequest = originalRequest;
}
newCall将OkHttpClient和request对象传进来,当然这里的copy复制了相关配置并且做了一些额外初始化。
先看execute方法做了什么
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.getDispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.getDispatcher().finished(this);
}
}
在Dispatcher中,executed方法将Call加入executedCalls队列。这个队列用于处理同步请求,同时readyCalls和runningCalls两个队列用于处理异步请求。
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.okhttp;
import com.squareup.okhttp.Call.AsyncCall;
import com.squareup.okhttp.internal.Util;
import com.squareup.okhttp.internal.http.HttpEngine;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Policy on when async requests are executed.
*
* Each dispatcher uses an {@link ExecutorService} to run calls internally. If you
* supply your own executor, it should be able to run {@linkplain #getMaxRequests the
* configured maximum} number of calls concurrently.
*/
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
/** Executes calls. Created lazily. */
private ExecutorService executorService;
/** Ready calls in the order they'll be run. */
private final Deque readyCalls = new ArrayDeque<>();
/** Running calls. Includes canceled calls that haven't finished yet. */
private final Deque runningCalls = new ArrayDeque<>();
/** In-flight synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque executedCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService getExecutorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
/**
* Set the maximum number of requests to execute concurrently. Above this
* requests queue in memory, waiting for the running calls to complete.
*
* If more than {@code maxRequests} requests are in flight when this is
* invoked, those requests will remain in flight.
*/
public synchronized void setMaxRequests(int maxRequests) {
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
this.maxRequests = maxRequests;
promoteCalls();
}
public synchronized int getMaxRequests() {
return maxRequests;
}
/**
* Set the maximum number of requests for each host to execute concurrently.
* This limits requests by the URL's host name. Note that concurrent requests
* to a single IP address may still exceed this limit: multiple hostnames may
* share an IP address or be routed through the same HTTP proxy.
*
* If more than {@code maxRequestsPerHost} requests are in flight when this
* is invoked, those requests will remain in flight.
*/
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
this.maxRequestsPerHost = maxRequestsPerHost;
promoteCalls();
}
public synchronized int getMaxRequestsPerHost() {
return maxRequestsPerHost;
}
synchronized void enqueue(AsyncCall call) {
if (runningCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningCalls.add(call);
getExecutorService().execute(call);
} else {
readyCalls.add(call);
}
}
/** Cancel all calls with the tag {@code tag}. */
public synchronized void cancel(Object tag) {
for (AsyncCall call : readyCalls) {
if (Util.equal(tag, call.tag())) {
call.cancel();
}
}
for (AsyncCall call : runningCalls) {
if (Util.equal(tag, call.tag())) {
call.get().canceled = true;
HttpEngine engine = call.get().engine;
if (engine != null) engine.disconnect();
}
}
for (Call call : executedCalls) {
if (Util.equal(tag, call.tag())) {
call.cancel();
}
}
}
/** Used by {@code AsyncCall#run} to signal completion. */
synchronized void finished(AsyncCall call) {
if (!runningCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
promoteCalls();
}
private void promoteCalls() {
if (runningCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator i = readyCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningCalls.add(call);
getExecutorService().execute(call);
}
if (runningCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(Call call) {
executedCalls.add(call);
}
/** Used by {@code Call#execute} to signal completion. */
synchronized void finished(Call call) {
if (!executedCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
public synchronized int getRunningCallCount() {
return runningCalls.size();
}
public synchronized int getQueuedCallCount() {
return readyCalls.size();
}
}
getResponseWithInterceptorChain这个方法去做真正的请求,请求完毕后调用client.getDispatcher().finished(),将这个Call从同步请求队列中移出。
先不管真正的请求在哪里去做,现在来看异步的请求怎么做。
在enqueue方法中(看上面dispacher代码),如果runningCalls队列的数量没有超出范围,就加入线程池执行,否则加入readyCalls队列。
任务的执行线程池单独贴出来
public synchronized ExecutorService getExecutorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
第一个参数,表示线程池中默认策略不保留线程,超过了等待时间就回收,第二个参数表示最大线程数量,第三四个参数表示等待60秒后线程被回收,第四个参数表示存储未被执行Runnable对象的队列,这里用SynchronousQueue,意思是如果这个queue里有一个Runnable对象,入队操作就会被阻塞,同样如果没有Runnable对象,出队操作就会被阻塞。在这个参数设置下,可以认为只要有新的Runnable加入,就立即分配线程去执行,如果没有空闲线程就创建一个并执行。
好了现在执行是执行了,那请问怎么回调回来呢。
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.getDispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
在enqueue的时候,把Call包装成了一个AsyncCall,而线程池执行的也是这个AsyncCall,我们看一下它的实现
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);
if (canceled) {
signalledCallback = true;
responseCallback.onFailure(originalRequest, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(engine.getRequest(), e);
}
} finally {
client.getDispatcher().finished(this);
}
}
}
原来也是在执行的时候调用了getResponseWithInterceptorChain,拿到结果了之后回调(子线程)。至于为什么它是一个Runnable以及为什么执行的时候调的是execute很简单就不说了,最后同样调用了client.getDispatcher().finished(this)
/** Used by {@code AsyncCall#run} to signal completion. */
synchronized void finished(AsyncCall call) {
if (!runningCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
promoteCalls();
}
private void promoteCalls() {
if (runningCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator i = readyCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningCalls.add(call);
getExecutorService().execute(call);
}
if (runningCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
这个函数干了两件事,第一件事是把执行完的AsyncCall从RunningCalls中出队,第二件事是尝试将ReadyCalls队列中的请求加入Running中并执行。
好了现在解释了同步和异步两种请求是怎么实现的,接下来看真正的请求在哪里做。
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
class ApplicationInterceptorChain implements Interceptor.Chain {
private final int index;
private final Request request;
private final boolean forWebSocket;
ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
this.index = index;
this.request = request;
this.forWebSocket = forWebSocket;
}
@Override public Connection connection() {
return null;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
if (index < client.interceptors().size()) {
// There's another interceptor in the chain. Call that.
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
return client.interceptors().get(index).intercept(chain);
} else {
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
}
这里贴个图
interceptors可以在请求发出去之前,和结果刚拿到之后,对request和response做处理。
处理完之后呢,最终都会走到getResponse这里
/**
* Performs the request and returns the response. May return null if this
* call was canceled.
*/
Response getResponse(Request request, boolean forWebSocket) throws IOException {
// Copy body metadata to the appropriate request headers.
RequestBody body = request.body();
if (body != null) {
Request.Builder requestBuilder = request.newBuilder();
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");
}
request = requestBuilder.build();
}
// Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null);
int followUpCount = 0;
while (true) {
if (canceled) {
engine.releaseConnection();
throw new IOException("Canceled");
}
try {
engine.sendRequest();
engine.readResponse();
} catch (RequestException e) {
// The attempt to interpret the request failed. Give up.
throw e.getCause();
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
HttpEngine retryEngine = engine.recover(e);
if (retryEngine != null) {
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e.getLastConnectException();
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
HttpEngine retryEngine = engine.recover(e, null);
if (retryEngine != null) {
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e;
}
Response response = engine.getResponse();
Request followUp = engine.followUpRequest();
if (followUp == null) {
if (!forWebSocket) {
engine.releaseConnection();
}
return response;
}
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (!engine.sameConnection(followUp.httpUrl())) {
engine.releaseConnection();
}
Connection connection = engine.close();
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null,
response);
}
}
做了一些请求头的默认配置,然后new出一个HttpEngine,通过它来进行
engine.sendRequest();
engine.readResponse();
这两步最重要的操作,这两个方法代码很多,就不贴了,sendRequest会先去匹配缓存中的response,然后尝试去调用connect()方法。
/** Connect to the origin server either directly or via a proxy. */
private void connect() throws RequestException, RouteException {
if (connection != null) throw new IllegalStateException();
if (routeSelector == null) {
address = createAddress(client, networkRequest);
try {
routeSelector = RouteSelector.get(address, networkRequest, client);
} catch (IOException e) {
throw new RequestException(e);
}
}
connection = createNextConnection();
Internal.instance.connectAndSetOwner(client, connection, this, networkRequest);
route = connection.getRoute();
}
这里的connectAndSetOwner调用的是OkhttpClient里面的Internal实现,会走到Connection这个类
/**
* Connects this connection if it isn't already. This creates tunnels, shares
* the connection with the connection pool, and configures timeouts.
*/
void connectAndSetOwner(OkHttpClient client, Object owner, Request request)
throws RouteException {
setOwner(owner);
if (!isConnected()) {
List connectionSpecs = route.address.getConnectionSpecs();
connect(client.getConnectTimeout(), client.getReadTimeout(), client.getWriteTimeout(),
request, connectionSpecs, client.getRetryOnConnectionFailure());
if (isFramed()) {
client.getConnectionPool().share(this);
}
client.routeDatabase().connected(getRoute());
}
setTimeouts(client.getReadTimeout(), client.getWriteTimeout());
}
又会走到这里的connect方法
void connect(int connectTimeout, int readTimeout, int writeTimeout, Request request,
List connectionSpecs, boolean connectionRetryEnabled) throws RouteException {
if (connected) throw new IllegalStateException("already connected");
RouteException routeException = null;
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
Proxy proxy = route.getProxy();
Address address = route.getAddress();
if (route.address.getSslSocketFactory() == null
&& !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication not supported: " + connectionSpecs));
}
while (!connected) {
try {
socket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.getSocketFactory().createSocket()
: new Socket(proxy);
connectSocket(connectTimeout, readTimeout, writeTimeout, request,
connectionSpecSelector);
connected = true; // Success!
} catch (IOException e) {
Util.closeQuietly(socket);
socket = null;
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException;
}
}
}
}
判断一下代理,ssl配置,然后会走到connectSocket这个方法
/** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
private void connectSocket(int connectTimeout, int readTimeout, int writeTimeout,
Request request, ConnectionSpecSelector connectionSpecSelector) throws IOException {
socket.setSoTimeout(readTimeout);
Platform.get().connectSocket(socket, route.getSocketAddress(), connectTimeout);
if (route.address.getSslSocketFactory() != null) {
connectTls(readTimeout, writeTimeout, request, connectionSpecSelector);
}
if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
socket.setSoTimeout(0); // Framed connection timeouts are set per-stream.
framedConnection = new FramedConnection.Builder(route.address.uriHost, true, socket)
.protocol(protocol).build();
framedConnection.sendConnectionPreface();
} else {
httpConnection = new HttpConnection(pool, this, socket);
}
}
又会走到Platform.get().connectSocket(socket, route.getSocketAddress(), connectTimeout);
当然后面回根据协议作进一步的操作,这里最简单的Http1.1就是
public void connectSocket(Socket socket, InetSocketAddress address,
int connectTimeout) throws IOException {
socket.connect(address, connectTimeout);
}
绕了这么大弯子,最终就是一个Socket连接。而且针对普通Http连接,包装了一个HttpConnection用于网络请求的读写。
所以,OkHttp并不是对HttpURLConnection的封装,而是自己实现了底层的请求,它的定位更像是HttpClient,HttpURLConnection这些做请求的工具,只是它对I/O读写进行了Okio的包装,并内置了线程池进行异步处理,而且同时支持Https,SPDY等协议
readResponse中的操作,实际上就是从byte流里面将header和body解析出来,当然还有Interceptor的处理。
到此,就分析完了整个执行流程。