OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
複製代碼
OkHttpClient client = new OkHttpClient();
client.newCall(new Request.Builder().url("http://api.github.com").build())
.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
}
});
複製代碼
new Request.Builder().url("http://api.github.com").build()
複製代碼
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
複製代碼
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;
}
複製代碼
public interface Call extends Cloneable {
...
void enqueue(Callback responseCallback);
...
}
複製代碼
@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));
}
複製代碼
eventListener.callStart(this);
複製代碼
client.dispatcher().enqueue(new AsyncCall(responseCallback));
複製代碼
public Dispatcher dispatcher() {
return dispatcher;
}
複製代碼
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
...
}
複製代碼
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
複製代碼
Dispatcher有兩個默認的東西java
enqueue的過程,是壓到隊列的過程,他並非讓你每個請求按順序執行,而是讓他們分部,按隊列執行,只是到必定程度的時候他們會等一等。他們的程度就是在這設置的。android
這兩個值均可以設置,若是你想讓請求一個一個按順序執行,就能夠把maxRequests設爲1。git
這個時候知道什麼是Dispatcher了,那看一下他的enqueue,剛纔已經說過enqueue是幹什麼了,就是讓他們並列執行,只是到頂峯的時候,再往隊列裏面存一存。程序員
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
複製代碼
final class AsyncCall extends NamedRunnable {...}
複製代碼
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
複製代碼
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
複製代碼
client.newCall(new Request.Builder().url("http://api.github.com").build())
.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
}
});
複製代碼
Response response = getResponseWithInterceptorChain();
複製代碼
獲取響應,經過攔截器的鏈,尚未執行,怎麼就有響應了?都在這裏面,可是往裏面講就很深了,待會說。github
如今總結一下,若是你是用enqueue這種方法,web
那麼這個到這就圓滿了,怎麼圓滿了?你先是把你的請求放到後臺,而後去作實際的網絡請求,而後把網絡請求結果返回回來。這是一個完整的過程。面試
除了enqueue還有一個execute,咱們用得不多,可是不是徹底不用,由於有的時候咱們已經在後臺了。好比,如今個人網絡請求失敗了,網絡請求因爲某種緣由失敗了,好比個人權限不足。我須要如今獲取個人token,去在線獲取個人token,獲取token以後,繼續進行請求,那麼我如今是在後臺的,我就不須要enqueue,我使用execute。算法
那麼我簡單看一下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);
}
}
複製代碼
這個就是他的大結構,api
如今說兩個東西:
OkHttpClient client = new OkHttpClient();
複製代碼
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {...}
複製代碼
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
...
}
複製代碼
public enum Protocol {
HTTP_1_0("http/1.0"),
HTTP_1_1("http/1.1"),
SPDY_3("spdy/3.1"),
HTTP_2("h2"),
...
}
複製代碼
public final class ConnectionSpec {
// This is nearly equal to the cipher suites supported in Chrome 51, current as of 2016-05-25.
// All of these suites are available on Android 7.0; earlier releases support a subset of these
// suites. https://github.com/square/okhttp/issues/1972
private static final CipherSuite[] APPROVED_CIPHER_SUITES = new CipherSuite[] {
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
// Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll
// continue to include them until better suites are commonly available. For example, none
// of the better cipher suites listed above shipped with Android 4.4 or Java 7.
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
};
/** A modern TLS connection with extensions like SNI and ALPN available. */
public static final ConnectionSpec MODERN_TLS = new Builder(true)
.cipherSuites(APPROVED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.supportsTlsExtensions(true)
.build();
/** A backwards-compatible fallback connection for interop with obsolete servers. */
public static final ConnectionSpec COMPATIBLE_TLS = new Builder(MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.supportsTlsExtensions(true)
.build();
/** Unencrypted, unauthenticated connections for {@code http:} URLs. */
public static final ConnectionSpec CLEARTEXT = new Builder(false).build();
...
}
複製代碼
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
複製代碼
public static final ConnectionSpec MODERN_TLS = new Builder(true)
.cipherSuites(APPROVED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.supportsTlsExtensions(true)
.build();
複製代碼
這些是一個統一的支持,由於他是一個可接受方案,你可以支持什麼,你可以接受什麼?至於對方怎麼給你,對方可以接受什麼,大家最終商討一個方案,他們是固定的。好比最終大家決定要麼用1.1,要麼用1.2,確定不能是既用1.1,又用1.2,可是商討過程,發的確定是一個列表,讓他去選。
COMPATIBLE_TLS是給比較舊的服務器用的,通常也不用。
public static final ConnectionSpec COMPATIBLE_TLS = new Builder(MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.supportsTlsExtensions(true)
.build();
複製代碼
public static final ConnectionSpec CLEARTEXT = new Builder(false).build();
複製代碼
咱們作android的人,可能對Socket瞭解不是不少,有些人可能用到,Socket是什麼呢?socket就是咱們tcp的端口,就是一個(ip-端口)對。
SocketFactory就是用來建立這個端口的。什麼叫建立端口呢?
同理SSLSocketFactory他是ssl鏈接的factory,爲何要分開呢?
http是沒有端口的,沒有所謂的http端口。由於他沒有面向鏈接,他不面向鏈接。有時候咱們說的比較順嘴,說什麼會http端口,其實他說的是tcp端口,http是沒有端口的。
public interface HostnameVerifier {
/** * Verify that the host name is an acceptable match with * the server's authentication scheme. * * @param hostname the host name * @param session SSLSession used on the connection to host * @return true if the host name is acceptable */
public boolean verify(String hostname, SSLSession session);
}
複製代碼
@Override
public boolean verify(String host, SSLSession session) {
try {
Certificate[] certificates = session.getPeerCertificates();
return verify(host, (X509Certificate) certificates[0]);
} catch (SSLException e) {
return false;
}
}
複製代碼
Certificate[] certificates = session.getPeerCertificates();
return verify(host, (X509Certificate) certificates[0]);
複製代碼
public boolean verify(String host, X509Certificate certificate) {
return verifyAsIpAddress(host)
? verifyIpAddress(host, certificate)
: verifyHostname(host, certificate);
}
複製代碼
return hostname.equals(pattern);
複製代碼
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
//這裏能夠加多個,越多越容易
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client =new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
複製代碼
OkHttpClient client =new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.authenticator(new Authenticator() {
@javax.annotation.Nullable
@Override
public Request authenticate(Route route, Response response) throws IOException {
return response.request().newBuilder().addHeader("Authorization","Basic 用戶名密碼的base64").build();
}
})
.build();
複製代碼
Dns SYSTEM = new Dns() {
@Override public List<InetAddress> lookup(String hostname) throws UnknownHostException {
if (hostname == null) throw new UnknownHostException("hostname == null");
try {
return Arrays.asList(InetAddress.getAllByName(hostname));
} catch (NullPointerException e) {
UnknownHostException unknownHostException =
new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
unknownHostException.initCause(e);
throw unknownHostException;
}
}
};
複製代碼
Arrays.asList(InetAddress.getAllByName(hostname));
複製代碼
爲何要單獨設置這個呢?由於他們是有一些安全上的風險的。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
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);
}
複製代碼
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);
複製代碼
interceptors.addAll(client.interceptors());
interceptors.addAll(client.networkInterceptors());
複製代碼
- 第一個,retry就是重試,FollowUp就是跟進,去跟進重定向的連接。
複製代碼
interceptors.add(retryAndFollowUpInterceptor);
複製代碼
public final class RetryAndFollowUpInterceptor implements Interceptor {
...
@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);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, 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, streamAllocation, 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, streamAllocation.route());
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);
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;
}
}
...
複製代碼
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
複製代碼
Connections:</strong> physical socket connections to remote servers. These are
potentially slow to establish so it is necessary to be able to cancel a connection
currently being connected.
Streams:</strong> logical HTTP request/response pairs that are layered on
connections. Each connection has its own allocation limit, which defines how many
concurrent streams that connection can carry. HTTP/1.x connections can carry 1 stream
at a time, HTTP/2 typically carry multiple.
Calls:</strong> a logical sequence of streams, typically an initial request and
its follow up requests. We prefer to keep all streams of a single call on the same
connection for better behavior and locality.
複製代碼
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
複製代碼
response = realChain.proceed(request, streamAllocation, null, null);
複製代碼
catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
複製代碼
private boolean recover(IOException e, StreamAllocation streamAllocation, boolean requestSendStarted, Request userRequest) {
streamAllocation.streamFailed(e);
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false;
// No more routes to attempt.
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
複製代碼
Request followUp = followUpRequest(response, streamAllocation.route());
複製代碼
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
複製代碼
request = followUp;
複製代碼
while (true) {...}
複製代碼
public final class RetryAndFollowUpInterceptor implements Interceptor {
...
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
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());
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();
}
...
}
複製代碼
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("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
複製代碼
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)));
複製代碼
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
複製代碼
public interface CookieJar {
/** A cookie jar that never accepts any cookies. */
CookieJar NO_COOKIES = new CookieJar() {
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
...
}
複製代碼
CookieJar NO_COOKIES = new CookieJar() {
Map map = new HashMap();
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
map.put(url,cookies)
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return cookies.get(url);
}
};
複製代碼
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
複製代碼
public static String userAgent() {
return "okhttp/3.10.0";
}
複製代碼
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)));
}
複製代碼
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
複製代碼
public Factory(long nowMillis, Request request, Response cacheResponse) {
this.nowMillis = nowMillis;
this.request = request;
this.cacheResponse = cacheResponse;
if (cacheResponse != null) {
this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
Headers headers = cacheResponse.headers();
for (int i = 0, size = headers.size(); i < size; i++) {
String fieldName = headers.name(i);
String value = headers.value(i);
if ("Date".equalsIgnoreCase(fieldName)) {
servedDate = HttpDate.parse(value);
servedDateString = value;
} else if ("Expires".equalsIgnoreCase(fieldName)) {
expires = HttpDate.parse(value);
} else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
lastModified = HttpDate.parse(value);
lastModifiedString = value;
} else if ("ETag".equalsIgnoreCase(fieldName)) {
etag = value;
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HttpHeaders.parseSeconds(value, -1);
}
}
}
}
複製代碼
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
複製代碼
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
複製代碼
有一行是newStream,什麼是steam?
那麼什麼叫Codec?
你的newStream建立的codec只是其一,他還會建立一個connection,建立一個鏈接,這個鏈接不是下面那一行建立的,下面那一行只是拿到。其實核心的只有一行代碼。
newStream咱們跳轉進去看一下。咱們會跳轉好幾回,不須要懂,只須要知道咱們講到了什麼東西。我想讓你知道他是怎麼跟tcp交互,怎麼跟ssl交互。因此咱們繼續找,newStream裏面有一個findHealthyConnection。
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
複製代碼
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
複製代碼
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
複製代碼
connectSocket(connectTimeout, readTimeout, call, eventListener);
複製代碼
總之能夠在這看到,okhttp他作了tcp鏈接。這跟咱們前幾年用的http庫徹底不同,他們全都用的要麼是Apache的HttpClient,或者是用了android自帶的HttpUrlConnection。他們都沒有本身作鏈接的,可是okhttp作了。
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
// The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
// More details:
// https://github.com/square/okhttp/issues/3245
// https://android-review.googlesource.com/#/c/271775/
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
複製代碼
} else {
connectSocket(connectTimeout, readTimeout, call, eventListener);
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
複製代碼
connectTls(connectionSpecSelector);
複製代碼
sslSocket.startHandshake();
複製代碼
他沒有後置工做。
首先看這個writeRequestHeaders,
httpCodec.writeRequestHeaders(request);
複製代碼
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
複製代碼
回到CallServerInterceptor,寫完請求以後,就會讀,讀響應裏面的東西。
responseBuilder = httpCodec.readResponseHeaders(false);
複製代碼
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
複製代碼
網絡問題在ConnectInterceptor出問題以後就會拐回來,不會往ConnectInterceptor走。
interceptors.addAll(client.interceptors());
interceptors.addAll(client.networkInterceptors());
複製代碼
OkHttpClient client =new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//前置工做
Response response = chain.proceed(chain.request());
//後置工做
return response;
}
})
.build();
複製代碼
至關於我在中間插了一個什麼都沒幹的中間人。若是我想作什麼事情,就能夠插入前置工做和後置工做。
而interceptors、networkInterceptors有什麼區別呢?
okhttp他的角色是什麼?他跟retrofit比起來?