使用okhttp来作为项目的网络请求库,这个估计是很多android开发者的选择。怎么用okhttp发起一个请求的代码网上一大堆,很简单。但是要理解它是怎么完成一次网络请求过程的,就必须读人家的源码,它的代码量还是很大的,功能也很庞大。只有读过源码后,用起来才更踏实,也能知道在网络请求的时候,我们还可以对它怎么优化,作为一个知名项目,它的代码架构设计也是值得我们学习的地方。
看这种功能巨复杂的库,还是得带着问题去看,先只读和问题有关的代码,否则的话,很容易看懵。
这篇文章里,解决一个问题:okhttp是如何完成一次网络请求的?中间还会穿插一些读源码时得到的小知识点,在开发中可能会用到的
我们先看ok发起一个请求的代码
OkHttpClient client=new OkHttpClient();
Request.Builder builder=new Request.Builder();
builder.url(url);
Call call=client.newCall(builder.build());
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call,final IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
一个http的GET请求这就可以完成了,这种写法是异步发起请求的方式。POST请求的过程,同步请求的过程,他们的处理会多出一些来,后面文章里会分析,接下来我们就到源码里看了。
OkHttpClient client=new OkHttpClient();
进入上面的这行代码里看看。
public OkHttpClient() {
this(new Builder());
}
private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
很显然这段代码在做变量初始化的工作,初始化的工作交给了一个Builder构造器去做。
小知识点
1.interceptors ,networkInterceptors 字段在OkHttpClient里是不可变长的list,所以有想给请求添加拦截器得朋友不要在OkHttpClient里直接加哦,否则会报错,要在builder里添加。
还有个问题,为什么这个类的变量初始化要交给一个builder去做,而且这个库里好多类都会这么写,这里我不想扯开讲,跑离我们的问题了,后续文章里我会去分析。
接下来我们分析请求里的这几行代码
Request.Builder builder=new Request.Builder();
builder.url(url);
Call call=client.newCall(builder.build());
这几行代码的作用也很明显,在把请求的信息封装到一个类里,封装的时候这里又用到了builder构造器。
对于一个GET请求,有个url就可以了,所以请求需要的信息就封装完毕。接下来就要发送请求了。
call.enqueue(new Callback() {
@Override
public void onFailure(final Call call,final IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
我们就要到enqueue里看看了,okhttp里设计了很多接口类,看源码的时候一定要找到对应的实现类里看。
这个call的实现类是RealCall这个类,那我们就要到RealCall里看看他的enqueue()方法
@Override public void enqueue(Callback responseCallback) {
enqueue(responseCallback, false);
}
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
这里我们可以看到请求又被封装成了AsyncCall,并且交给OkHttpClient 的dispatcher去执行了。
这里有两个问题要弄清:1.AsyncCall是起到什么作用的? 2.dispatcher是干什么用的?
首先来看AsyncCall类
1.它是RealCall的内部类,它可以获取到Realcall里所有信息,所以创建它的时候没必要传入请求信息了。
2.看看AsyncCall的父类
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = String.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();
}
看到Runnable就应该明白了,AsyncCall就是在为一次请求开启一个子线程,AsyncCall的子类实现execute()方法就行,把请求的过程扔到里面就行。这就解决了刚刚提出的第一个问题。
3.接下来就解决第二个问题dispatcher是干嘛的。
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
/** Executes calls. Created lazily. */
private ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
}
看到ExecutorService 就应该明白了,AsyncCall是个Runnable的子类,Dispatcher里封装好了线程池类ExecutorService ,那Dispatcher得作用就是去执行一个个的请求子线程喽。其他字段一看也就能明白他的意思,不再分析。
接下来我们就要分AsyncCall的execute()方法了。
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket);
if (canceled) {
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!
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
getResponseWithInterceptorChain(forWebSocket);得到我们请求的响应封装类,然后交给我们的回调类Callback进行成功或者失败的回调。
小知识点
1.从execute()方法中可以看出,我们的回调也处于一个子线程中,所以在成功,失败的回调时,不能进行ui操作。
接下来,我们再去看看他是怎么得到一个响应体。
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
}
class ApplicationInterceptorChain implements Interceptor.Chain {
.....
@Override public Response proceed(Request request) throws IOException {
// If there's another interceptor in the chain, call that.
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
}
在proceed(Request request)方法中,我们可以看到,这里有对网络进行拦截的地方,拦截器会在执行请求前执行,这里我们不去分析拦截器了,直接看getResponse(request, forWebSocket);
/**
* Performs the request and returns the response. May return null if this call was canceled.
*/
Response getResponse(Request request, boolean forWebSocket) throws IOException {
......
// 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);
int followUpCount = 0;
while (true) {
if (canceled) {
engine.releaseStreamAllocation();
throw new IOException("Canceled");
}
boolean releaseConnection = true;
try {
**engine.sendRequest();**
**engine.readResponse();**
releaseConnection = false;
} 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.getLastConnectException(), null);
if (retryEngine != null) {
releaseConnection = false;
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) {
releaseConnection = false;
engine = retryEngine;
continue;
}
// Give up; recovery is not possible.
throw e;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
StreamAllocation streamAllocation = engine.close();
streamAllocation.release();
}
}
Response response = engine.getResponse();
Request followUp = engine.followUpRequest();
if (followUp == null) {
if (!forWebSocket) {
engine.releaseStreamAllocation();
}
return response;
}
StreamAllocation streamAllocation = engine.close();
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (!engine.sameConnection(followUp.url())) {
streamAllocation.release();
streamAllocation = null;
}
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
response);
}
}
这里着重看下面两行代码,一个是发送请求,一个是读取响应
engine.sendRequest();
engine.readResponse();
public void sendRequest() throws RequestException, RouteException, IOException {
if (cacheStrategy != null) return; // Already sent.
if (httpStream != null) throw new IllegalStateException();
Request request = networkRequest(userRequest);
InternalCache responseCache = Internal.instance.internalCache(client);
Response cacheCandidate = responseCache != null
? responseCache.get(request)
: null;
long now = System.currentTimeMillis();
cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
networkRequest = cacheStrategy.networkRequest;
cacheResponse = cacheStrategy.cacheResponse;
if (responseCache != null) {
responseCache.trackResponse(cacheStrategy);
}
if (cacheCandidate != null && cacheResponse == null) {
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.
if (networkRequest == null && cacheResponse == null) {
userResponse = new Response.Builder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_BODY)
.build();
return;
}
// If we don't need the network, we're done.
if (networkRequest == null) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.build();
userResponse = unzip(userResponse);
return;
}
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean success = false;
try {
httpStream = connect();
httpStream.setHttpEngine(this);
if (writeRequestHeadersEagerly()) {
long contentLength = OkHeaders.contentLength(request);
if (bufferRequestBody) {
if (contentLength > Integer.MAX_VALUE) {
throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
+ "setChunkedStreamingMode() for requests larger than 2 GiB.");
}
if (contentLength != -1) {
// Buffer a request body of a known length.
httpStream.writeRequestHeaders(networkRequest);
requestBodyOut = new RetryableSink((int) contentLength);
} else {
// Buffer a request body of an unknown length. Don't write request headers until the
// entire body is ready; otherwise we can't set the Content-Length header correctly.
requestBodyOut = new RetryableSink();
}
} else {
httpStream.writeRequestHeaders(networkRequest);
requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
}
}
success = true;
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (!success && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
}
在这里它会有读取缓存内容的过程,这里我们先不管,假设是第一次请求,什么缓存都没有,然后我们就会到 httpStream = connect();方法
private HttpStream connect() throws RouteException, RequestException, IOException {
boolean doExtensiveHealthChecks = !networkRequest.method().equals("GET");
return streamAllocation.newStream(client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis(),
client.retryOnConnectionFailure(), doExtensiveHealthChecks);
}
public HttpStream newStream(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws RouteException, IOException {
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpStream resultStream;
if (resultConnection.framedConnection != null) {
resultStream = new Http2xStream(this, resultConnection.framedConnection);
} else {
resultConnection.socket().setSoTimeout(readTimeout);
resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
resultStream = new Http1xStream(this, resultConnection.source, resultConnection.sink);
}
synchronized (connectionPool) {
stream = resultStream;
return resultStream;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
findHealthyConnection()这个方法是进行请求链接,链接有的是已经缓存了的,看下面的方法
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException, RouteException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// Otherwise do a potentially-slow check to confirm that the pooled connection is still good.
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate;
}
connectionFailed(new IOException());
}
}
/**
* Returns a connection to host a new stream. This prefers the existing connection if it exists,
* then the pool, finally building a new connection.
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException, RouteException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (stream != null) throw new IllegalStateException("stream != null");
if (canceled) throw new IOException("Canceled");
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// Attempt to get a connection from the pool.
RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this);
if (pooledConnection != null) {
this.connection = pooledConnection;
return pooledConnection;
}
selectedRoute = route;
}
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
synchronized (connectionPool) {
route = selectedRoute;
}
}
RealConnection newConnection = new RealConnection(selectedRoute);
acquire(newConnection);
synchronized (connectionPool) {
Internal.instance.put(connectionPool, newConnection);
this.connection = newConnection;
if (canceled) throw new IOException("Canceled");
}
newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
connectionRetryEnabled);
routeDatabase().connected(newConnection.route());
return newConnection;
}
从上面的方法可以看出,它首先会从 Internal.instance里取缓存的链接,没有就自己创建一个出来,然后存到 Internal.instance。接下来看看链接的执行方法
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) throws RouteException {
if (protocol != null) throw new IllegalStateException("already connected");
RouteException routeException = null;
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
Proxy proxy = route.proxy();
Address address = route.address();
if (route.address().sslSocketFactory() == null
&& !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication not supported: " + connectionSpecs));
}
while (protocol == null) {
try {
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector);
} catch (IOException e) {
closeQuietly(socket);
closeQuietly(rawSocket);
socket = null;
rawSocket = null;
source = null;
sink = null;
handshake = null;
protocol = null;
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException;
}
}
}
}
从下面代码,我们看到了Socket,也就是说okhttp的请求是通过长连接Socket来实现
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector);
接下来再看看
/** 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,
ConnectionSpecSelector connectionSpecSelector) throws IOException {
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
throw new ConnectException("Failed to connect to " + route.socketAddress());
}
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
if (route.address().sslSocketFactory() != null) {
connectTls(readTimeout, writeTimeout, connectionSpecSelector);
} else {
protocol = Protocol.HTTP_1_1;
socket = rawSocket;
}
if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
socket.setSoTimeout(0); // Framed connection timeouts are set per-stream.
FramedConnection framedConnection = new FramedConnection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.protocol(protocol)
.listener(this)
.build();
framedConnection.sendConnectionPreface();
// Only assign the framed connection once the preface has been sent successfully.
this.allocationLimit = framedConnection.maxConcurrentStreams();
this.framedConnection = framedConnection;
} else {
this.allocationLimit = 1;
}
}
public void connectSocket(Socket socket, InetSocketAddress address,
int connectTimeout) throws IOException {
socket.connect(address, connectTimeout);
}
上面到了我们熟悉的Socket链接,
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
source封装了网络的输入流,sink封装了网络请求的输出流,这些是我们熟悉的了。
Socket长连接我们都会用,为甚么我们就写不出这么强大的东西来呢,连读懂人家的东西都要费好大劲,呵呵呵,学无止境啊。
这里我们只分析到HttpEngine的sendRequest()里,接下来就分析它的readResponse()方法了。
public void readResponse() throws IOException {
if (userResponse != null) {
return; // Already ready.
}
if (networkRequest == null && cacheResponse == null) {
throw new IllegalStateException("call sendRequest() first!");
}
if (networkRequest == null) {
return; // No network response to read.
}
Response networkResponse;
if (forWebSocket) {
httpStream.writeRequestHeaders(networkRequest);
networkResponse = readNetworkResponse();
} else if (!callerWritesRequestBody) {
networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
} else {
// Emit the request body's buffer so that everything is in requestBodyOut.
if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
bufferedRequestBody.emit();
}
// Emit the request headers if we haven't yet. We might have just learned the Content-Length.
if (sentRequestMillis == -1) {
if (OkHeaders.contentLength(networkRequest) == -1
&& requestBodyOut instanceof RetryableSink) {
long contentLength = ((RetryableSink) requestBodyOut).contentLength();
networkRequest = networkRequest.newBuilder()
.header("Content-Length", Long.toString(contentLength))
.build();
}
httpStream.writeRequestHeaders(networkRequest);
}
// Write the request body to the socket.
if (requestBodyOut != null) {
if (bufferedRequestBody != null) {
// This also closes the wrapped requestBodyOut.
bufferedRequestBody.close();
} else {
requestBodyOut.close();
}
if (requestBodyOut instanceof RetryableSink) {
httpStream.writeRequestBody((RetryableSink) requestBodyOut);
}
}
networkResponse = readNetworkResponse();
}
receiveHeaders(networkResponse.headers());
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (validate(cacheResponse, networkResponse)) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
releaseStreamAllocation();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
InternalCache responseCache = Internal.instance.internalCache(client);
responseCache.trackConditionalCacheHit();
responseCache.update(cacheResponse, stripBody(userResponse));
userResponse = unzip(userResponse);
return;
} else {
closeQuietly(cacheResponse.body());
}
}
userResponse = networkResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (hasBody(userResponse)) {
maybeCache();
userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
}
}
在readResponse()中会执行到这句代码
networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
上面的proceed()方法很重要,里面是封装响应类,这里也是post提交参数的地方
@Override public Response proceed(Request request) throws IOException {
calls++;
if (index > 0) {
Interceptor caller = client.networkInterceptors().get(index - 1);
Address address = connection().route().address();
// Confirm that the interceptor uses the connection we've already prepared.
if (!request.url().host().equals(address.url().host())
|| request.url().port() != address.url().port()) {
throw new IllegalStateException("network interceptor " + caller
+ " must retain the same host and port");
}
// Confirm that this is the interceptor's first call to chain.proceed().
if (calls > 1) {
throw new IllegalStateException("network interceptor " + caller
+ " must call proceed() exactly once");
}
}
if (index < client.networkInterceptors().size()) {
// There's another interceptor in the chain. Call that.
NetworkInterceptorChain chain = new NetworkInterceptorChain(index + 1, request);
Interceptor interceptor = client.networkInterceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
// Confirm that the interceptor made the required call to chain.proceed().
if (chain.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
if (interceptedResponse == null) {
throw new NullPointerException("network interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
httpStream.writeRequestHeaders(request);
//Update the networkRequest with the possibly updated interceptor request.
networkRequest = request;
//这个判断里,要是post请求的话,会提交这里面的参数,参数通过请求的输出流传出去
if (permitsRequestBody(request) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
Response response = readNetworkResponse();
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;
}
}
post提交的处理在上面注释了,提交的参数可以使文字或者图片,都是通过流传出去,这里不细具体的源码了,看readNetworkResponse();这个方法
private Response readNetworkResponse() throws IOException {
httpStream.finishRequest();
Response networkResponse = httpStream.readResponseHeaders()
.request(networkRequest)
.handshake(streamAllocation.connection().handshake())
.header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
.header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
.build();
if (!forWebSocket) {
networkResponse = networkResponse.newBuilder()
.body(httpStream.openResponseBody(networkResponse))
.build();
}
if ("close".equalsIgnoreCase(networkResponse.request().header("Connection"))
|| "close".equalsIgnoreCase(networkResponse.header("Connection"))) {
streamAllocation.noNewStreams();
}
return networkResponse;
}
在下面的代码中,把请求的输入流封装到了响应的body里,输入流正是我们想要的请求内容啊
if (!forWebSocket) {
networkResponse = networkResponse.newBuilder()
.body(httpStream.openResponseBody(networkResponse))
.build();
}
在readNetworkResponse()方法执行完,还有下面这波操作,解压缩流,okhttp是支持gzip压缩流,要不人家效率高呢,这里是看到了。
if (hasBody(userResponse)) {
maybeCache();
userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
}
好了,到这里,一个完整的请求就完了,从发送请求到读响应,层层封装,如果要有深刻理解,非亲自走一遍这个代码流程不可,而且我这个分析过程已经省略了好多东西,直奔问题去的,细度的话,收获会更多。