看了两天OKhttp的请求流程及拦截器,觉得有必要写一下,巩固一下。
1、OKhttp如何发送请求?
2、如何去处理这些请求的?
首先我们来看一下OKhttp的简单使用?
异步请求:
private void studyOkHttp(){ OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); Request request = new Request.Builder().url("https://www.baidu.com").build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()){ KLog.i(response.body().string()); } } }); } 复制代码
代码我就不解释了,大家应该都能够看懂。
下面我们来看一下HttpUrlConnection如何使用,因为大家可能对这个会比较熟悉,结合着这个,想必更加的好理解。
private void studyHttpUrlConnection(){ try { URL url = new URL("https://www.baidu.com"); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); //设置连接超时,2000ms urlConnection.setConnectTimeout(2000); //设置指定时间内服务器没有返回数据的超时,5000ms urlConnection.setReadTimeout(5000); //设置参数 urlConnection.setDoOutput(true); //需要输出 urlConnection.setDoInput(true); //需要输入 urlConnection.setUseCaches(false); //不允许缓存 urlConnection.setRequestMethod("POST"); //设置请求属性,给请求头添加东西 urlConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); urlConnection.setRequestProperty("accept", "application/json"); urlConnection.setRequestProperty("Connection", "Keep-Alive");// 维持长连接 urlConnection.setRequestProperty("Charset", "UTF-8"); int resultCode = urlConnection.getResponseCode();//获取响应码 if (HttpURLConnection.HTTP_OK == resultCode) {//表示请求成功 } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } 复制代码
从上面代码可以看出,请求无非就三步
创建一个Request
创建一个OKhttpClient
通过OKhttpClient把Request发送给服务端,然后拿到Response,Response中有我们需要的一切东西
那么这个过程就算完成了。整个过程很方便,你所需要的一般都会满足。所以要一探究竟。
我们进入OKhttpClient的newCall方法
看到如下方法
@Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); } 复制代码
返回一个Call,RealCall应该就是Call的实现,接着进入newRealCall方法
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; } 复制代码
紧接着又调用了 void enqueue(Callback responseCallback);方法,由于RealCall实现了Call,所以真正执行的就是RealCall中的enqueue方法
@Override public Response execute() throws IOException { //。。。省略了部分代码 try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { e = timeoutExit(e); eventListener.callFailed(this, e); throw e; } finally { client.dispatcher().finished(this); } } 复制代码
Response result = getResponseWithInterceptorChain();
从这一行代码我们就可以拿到Response,感觉没有做什么东西,就拿到了结果,所以我们来看一下**getResponseWithInterceptorChain()**方法
进入getResponseWithInterceptorChain()方法
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); } 复制代码
从这里我们可以看到,创建了一个拦截器数组,分别放入了
我们来看
chain.proceed( originalRequest ),也就是RealInterceptorChain中的proceed中的方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection 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. if (this.httpCodec != null && !this.connection.supportsUrl(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(). 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, call, eventListener, connectTimeout, readTimeout,writeTimeout); 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"); } if (response.body() == null) { throw new IllegalStateException( "interceptor " + interceptor + " returned a response with no body"); } return response; } 复制代码
代码不多,我就都贴出来了,我们主要看这几行代码
// Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); 复制代码
执行proceed时,index传进来的是0,所以 interceptors 获取到第一个拦截器,一般是我们自己定义的,如果没有定义,那就回获取到RetryAndFollowUpInterceptor拦截器,然后就会调用RetryAndFollowUpInterceptor的intercept方法,我们进去看看
@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; 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) { } //省略了多行代码 } 复制代码
realChain目前是RealInterceptorChain,然后又重新调用了proceed方法,也就是说用重新调用了RealInterceptorChain中的proceed中的方法,所以又回来了,
由于在第一次调用的时候,index传进来是0,在这里,又重新创建了一个RealInterceptorChain ,所以在RetryAndFollowUpInterceptor获取的到RealInterceptorChain 已经变成了1,然后紧接着获取 interceptors 中第二个拦截器,也就是BridgeInterceptor,紧接着又执行了BridgeInterceptor中的intercept方法,我们去BridgeInterceptor中看这个方法
@Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); Response networkResponse = chain.proceed(requestBuilder.build()); return responseBuilder.build(); } 复制代码
很熟悉的影子chain.proceed(requestBuilder.build());又看到了这个方法,也就是说用重新调用了RealInterceptorChain中的proceed中的方法,所以又回到了RealInterceptorChain 中的proceed中,这时候index已经变成了2,就可以得到CacheInterceptor拦截器,以此类推,会分别轮询一遍拦截器
RetryAndFollowUpInterceptor -> BridgeInterceptor -> CacheInterceptor -> ConnectInterceptor -> CallServerInterceptor。
要想拿到RetryAndFollowUpInterceptor的Response,就需要拿到BridgeInterceptor 的Response,要想拿到BridgeInterceptor的Response,就需要拿到CacheInterceptor 的Response,所以最终先拿到CallServerInterceptor的Response返回给ConnectInterceptor,依次类推,就是这样的一个过程。
各个拦截器中都做了什么操作呢?可以参考如下两篇文章
文章一
文章二
所以我们来分析一下日志拦截器HttpLoggingInterceptor,我们看一下intercept方法
@Override public Response intercept(Chain chain) throws IOException { Level level = this.level; //获取一个Request Request request = chain.request(); //如果设置了NONE,就不会打印,直接去调用下一个拦截器 if (level == Level.NONE) { return chain.proceed(request); } boolean logBody = level == Level.BODY; boolean logHeaders = logBody || level == Level.HEADERS; //获取请求体 RequestBody requestBody = request.body(); boolean hasRequestBody = requestBody != null; //获取Connection Connection connection = chain.connection(); String requestStartMessage = "--> " + request.method() + ' ' + request.url() + (connection != null ? " " + connection.protocol() : ""); if (!logHeaders && hasRequestBody) { requestStartMessage += " (" + requestBody.contentLength() + "-byte body)"; //打印请求方法 请求rul 请求端口 请求的字节长度 } logger.log(requestStartMessage); if (logHeaders) { if (hasRequestBody) { // Request body headers are only present when installed as a network interceptor. Force // them to be included (when available) so there values are known. if (requestBody.contentType() != null) { logger.log("Content-Type: " + requestBody.contentType()); } if (requestBody.contentLength() != -1) { logger.log("Content-Length: " + requestBody.contentLength()); } } //获取Header Headers headers = request.headers(); for (int i = 0, count = headers.size(); i < count; i++) { String name = headers.name(i); // Skip headers from the request body as they are explicitly logged above. if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) { logger.log(name + ": " + headers.value(i)); //获取header的名字和值 } } if (!logBody || !hasRequestBody) { logger.log("--> END " + request.method()); } else if (bodyHasUnknownEncoding(request.headers())) { logger.log("--> END " + request.method() + " (encoded body omitted)"); } else { Buffer buffer = new Buffer(); requestBody.writeTo(buffer); Charset charset = UTF8; MediaType contentType = requestBody.contentType(); if (contentType != null) { charset = contentType.charset(UTF8); } logger.log(""); if (isPlaintext(buffer)) { logger.log(buffer.readString(charset)); logger.log("--> END " + request.method() + " (" + requestBody.contentLength() + "-byte body)"); } else { logger.log("--> END " + request.method() + " (binary " + requestBody.contentLength() + "-byte body omitted)"); } } } long startNs = System.nanoTime(); Response response; try { response = chain.proceed(request); //这个地方要注意一下,每个拦截器中都会调用这个方法,如果没有这个方法,那就不会继续访问其他的拦截器了 } catch (Exception e) { logger.log("<-- HTTP FAILED: " + e); throw e; } long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); ResponseBody responseBody = response.body(); long contentLength = responseBody.contentLength(); String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length"; logger.log("<-- " + response.code() + (response.message().isEmpty() ? "" : ' ' + response.message()) + ' ' + response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", " + bodySize + " body" : "") + ')'); if (logHeaders) { Headers headers = response.headers(); for (int i = 0, count = headers.size(); i < count; i++) { logger.log(headers.name(i) + ": " + headers.value(i)); } if (!logBody || !HttpHeaders.hasBody(response)) { logger.log("<-- END HTTP"); } else if (bodyHasUnknownEncoding(response.headers())) { logger.log("<-- END HTTP (encoded body omitted)"); } else { BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); // Buffer the entire body. Buffer buffer = source.buffer(); Long gzippedLength = null; if ("gzip".equalsIgnoreCase(headers.get("Content-Encoding"))) { gzippedLength = buffer.size(); GzipSource gzippedResponseBody = null; try { gzippedResponseBody = new GzipSource(buffer.clone()); buffer = new Buffer(); buffer.writeAll(gzippedResponseBody); } finally { if (gzippedResponseBody != null) { gzippedResponseBody.close(); } } } Charset charset = UTF8; MediaType contentType = responseBody.contentType(); if (contentType != null) { charset = contentType.charset(UTF8); } if (!isPlaintext(buffer)) { logger.log(""); logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)"); return response; } if (contentLength != 0) { logger.log(""); logger.log(buffer.clone().readString(charset)); } if (gzippedLength != null) { logger.log("<-- END HTTP (" + buffer.size() + "-byte, " + gzippedLength + "-gzipped-byte body)"); } else { logger.log("<-- END HTTP (" + buffer.size() + "-byte body)"); } } } return response; } 复制代码
这样就会把请求的body以及返回的Response中的信息打印出来了,就是这样的一个流程。不知道大家明白没有