美文网首页一些收藏Android开发Android技术知识
Android高工面试被问OKHTTP内核解析,我慌了!

Android高工面试被问OKHTTP内核解析,我慌了!

作者: 今日Android | 来源:发表于2020-11-04 16:08 被阅读0次

    基本使用

    从使用方法出发,首先是怎么使用,其次是我们使用的功能在内部是如何实现的.建议大家下载 OkHttp 源码之后,跟着本文,过一遍源码。

    官方博客栗子:http://square.github.io/okhttp/#examples

    1. OkHttpClient client = new OkHttpClient();

    2. String run(String url) throws IOException {

    3. Request request = new Request.Builder()

    4. .url(url)

    5. .build();

    6. Response response = client.newCall(request).execute();

    7. return response.body().string();

    8. }

    Request、Response、Call 基本概念

    上面的代码中涉及到几个常用的类:Request、Response和Call。下面分别介绍:

    Request

    每一个HTTP请求包含一个URL、一个方法(GET或POST或其他)、一些HTTP头。请求还可能包含一个特定内容类型的数据类的主体部分。

    Response

    响应是对请求的回复,包含状态码、HTTP头和主体部分。

    Call

    OkHttp使用Call抽象出一个满足请求的模型,尽管中间可能会有多个请求或响应。执行Call有两种方式,同步或异步

    第一步:创建 OkHttpClient对象,进行源码分析:

    1. OkHttpClient client = new OkHttpClient();

    通过okhttp源码分析,直接创建的 OkHttpClient对象并且默认构造builder对象进行初始化

    1. public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

    2. public OkHttpClient() {

    3. this(new Builder());

    4. }

    5. OkHttpClient(Builder builder) {

    6. this.dispatcher = builder.dispatcher;

    7. this.proxy = builder.proxy;

    8. this.protocols = builder.protocols;

    9. this.connectionSpecs = builder.connectionSpecs;

    10. this.interceptors = Util.immutableList(builder.interceptors);

    11. this.networkInterceptors = Util.immutableList(builder.networkInterceptors);

    12. this.eventListenerFactory = builder.eventListenerFactory;

    13. this.proxySelector = builder.proxySelector;

    14. this.cookieJar = builder.cookieJar;

    15. this.cache = builder.cache;

    16. this.internalCache = builder.internalCache;

    17. this.socketFactory = builder.socketFactory;

    18. boolean isTLS = false;

    19. ......

    20. this.hostnameVerifier = builder.hostnameVerifier;

    21. this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(

    22. certificateChainCleaner);

    23. this.proxyAuthenticator = builder.proxyAuthenticator;

    24. this.authenticator = builder.authenticator;

    25. this.connectionPool = builder.connectionPool;

    26. this.dns = builder.dns;

    27. this.followSslRedirects = builder.followSslRedirects;

    28. this.followRedirects = builder.followRedirects;

    29. this.retryOnConnectionFailure = builder.retryOnConnectionFailure;

    30. this.connectTimeout = builder.connectTimeout;

    31. this.readTimeout = builder.readTimeout;

    32. this.writeTimeout = builder.writeTimeout;

    33. this.pingInterval = builder.pingInterval;

    34. }

    35. }

    第二步:接下来发起 HTTP 请求

    1. Request request = new Request.Builder().url("url").build();

    2. okHttpClient.newCall(request).enqueue(new Callback() {

    3. @Override

    4. public void onFailure(Call call, IOException e) {

    5. }

    6. @Override

    7. public void onResponse(Call call, Response response) throws IOException {

    8. }

    9. });

    第二步:代码流程分析:

    1. Request request = new Request.Builder().url("url").build();

    初始化构建者模式和请求对象,并且用URL替换Web套接字URL。

    1. public final class Request {

    2. public Builder() {

    3. this.method = "GET";

    4. this.headers = new Headers.Builder();

    5. }

    6. public Builder url(String url) {

    7. ......

    8. // Silently replace web socket URLs with HTTP URLs.

    9. if (url.regionMatches(true, 0, "ws:", 0, 3)) {

    10. url = "http:" + url.substring(3);

    11. } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {

    12. url = "https:" + url.substring(4);

    13. }

    14. HttpUrl parsed = HttpUrl.parse(url);

    15. ......

    16. return url(parsed);

    17. }

    18. public Request build() {

    19. ......

    20. return new Request(this);

    21. }

    22. }

    第三步:方法解析:

    1. okHttpClient.newCall(request).enqueue(new Callback() {

    2. @Override

    3. public void onFailure(Call call, IOException e) {

    4. }

    5. @Override

    6. public void onResponse(Call call, Response response) throws IOException {

    7. }

    8. });

    源码分析:

    1. public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

    2. @Override

    3. public Call newCall(Request request) {

    4. return new RealCall(this, request, false /* for web socket */);

    5. }

    6. }

    RealCall实现了Call.Factory接口创建了一个RealCall的实例,而RealCall是Call接口的实现。

    异步请求的执行流程

    1. final class RealCall implements Call {
    2. @Override
    3. public void enqueue(Callback responseCallback) {
    4. synchronized (this) {
    5. if (executed) throw new IllegalStateException("Already Executed");
    6. executed = true;
    7. }
    8. captureCallStackTrace();
    9. client.dispatcher().enqueue(new AsyncCall(responseCallback));
    10. }
    11. }

    由以上源码得知:

    1) 检查这个 call 是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一样的 call,可以利用 call#clone 方法进行克隆。

    2)利用 client.dispatcher().enqueue(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一

    3)AsyncCall是RealCall的一个内部类并且继承NamedRunnable,那么首先看NamedRunnable类是什么样的,如下:

    1. public abstract class NamedRunnable implements Runnable {

    2. ......

    3. @Override

    4. public final void run() {

    5. ......

    6. try {

    7. execute();

    8. }

    9. ......

    10. }

    11. protected abstract void execute();

    12. }

    可以看到NamedRunnable实现了Runnbale接口并且是个抽象类,其抽象方法是execute(),该方法是在run方法中被调用的,这也就意味着NamedRunnable是一个任务,并且其子类应该实现execute方法。下面再看AsyncCall的实现:

    1. final class AsyncCall extends NamedRunnable {

    2. private final Callback responseCallback;

    3. AsyncCall(Callback responseCallback) {

    4. super("OkHttp %s", redactedUrl());

    5. this.responseCallback = responseCallback;

    6. }

    7. ......

    8. final class RealCall implements Call {

    9. @Override protected void execute() {

    10. boolean signalledCallback = false;

    11. try {

    12. Response response = getResponseWithInterceptorChain();

    13. if (retryAndFollowUpInterceptor.isCanceled()) {

    14. signalledCallback = true;

    15. responseCallback.onFailure(RealCall.this, new IOException("Canceled"));

    16. } else {

    17. signalledCallback = true;

    18. responseCallback.onResponse(RealCall.this, response);

    19. }

    20. } catch (IOException e) {

    21. ......

    22. responseCallback.onFailure(RealCall.this, e);

    23. } finally {

    24. client.dispatcher().finished(this);

    25. }

    26. }

    AsyncCall实现了execute方法,首先是调用getResponseWithInterceptorChain()方法获取响应,然后获取成功后,就调用回调的onReponse方法,如果失败,就调用回调的onFailure方法。最后,调用Dispatcher的finished方法。

    关键代码:

    responseCallback.onFailure(RealCall.this, new IOException(“Canceled”));

    responseCallback.onResponse(RealCall.this, response);

    走完这两句代码会进行回调到刚刚我们初始化Okhttp的地方,如下:

    1. okHttpClient.newCall(request).enqueue(new Callback() {

    2. @Override

    3. public void onFailure(Call call, IOException e) {

    4. }

    5. @Override

    6. public void onResponse(Call call, Response response) throws IOException {

    7. }

    8. });

    核心重点类Dispatcher线程池介绍

    1. public final class Dispatcher {

    2. /** 最大并发请求数为64 */

    3. private int maxRequests = 64;

    4. /** 每个主机最大请求数为5 */

    5. private int maxRequestsPerHost = 5;

    6. /** 线程池 */

    7. private ExecutorService executorService;

    8. /** 准备执行的请求 */

    9. private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    10. /** 正在执行的异步请求,包含已经取消但未执行完的请求 */

    11. private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    12. /** 正在执行的同步请求,包含已经取消单未执行完的请求 */

    13. private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    在OkHttp,使用如下构造了单例线程池

    1. public synchronized ExecutorService executorService() {
    2. if (executorService == null) {
    3. executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
    4. new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    5. }
    6. return executorService;
    7. }

    构造一个线程池ExecutorService:

    1. executorService = new ThreadPoolExecutor(
    2. //corePoolSize 最小并发线程数,如果是0的话,空闲一段时间后所有线程将全部被销毁
    3. 0,
    4. //maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理
    5. Integer.MAX_VALUE,
    6. //keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间
    7. 60,
    8. //单位秒
    9. TimeUnit.SECONDS,
    10. //工作队列,先进先出
    11. new SynchronousQueue<Runnable>(),
    12. //单个线程的工厂
    13. Util.threadFactory("OkHttp Dispatcher", false));

    可以看出,在Okhttp中,构建了一个核心为[0, Integer.MAX_VALUE]的线程池,它不保留任何最小线程数,随时创建更多的线程数,当线程空闲时只能活60秒,它使用了一个不存储元素的阻塞工作队列,一个叫做”OkHttp Dispatcher”的线程工厂。

    也就是说,在实际运行中,当收到10个并发请求时,线程池会创建十个线程,当工作完成后,线程池会在60s后相继关闭所有线程。

    1. synchronized void enqueue(AsyncCall call) {
    2. if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    3. runningAsyncCalls.add(call);
    4. executorService().execute(call);
    5. } else {
    6. readyAsyncCalls.add(call);
    7. }
    8. }

    从上述源码分析,如果当前还能执行一个并发请求,则加入 runningAsyncCalls ,立即执行,否则加入 readyAsyncCalls 队列。

    Dispatcher线程池总结

    1)调度线程池Disptcher实现了高并发,低阻塞的实现 2)采用Deque作为缓存,先进先出的顺序执行 3)任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能

    这里是分析OkHttp源码,并不详细讲线程池原理,如对线程池不了解请参考如下链接

    点我,线程池原理,在文章性能优化最后有视频对线程池原理讲解

    1. try {
    2. Response response = getResponseWithInterceptorChain();
    3. if (retryAndFollowUpInterceptor.isCanceled()) {
    4. signalledCallback = true;
    5. responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    6. } else {
    7. signalledCallback = true;
    8. responseCallback.onResponse(RealCall.this, response);
    9. }
    10. } finally {
    11. client.dispatcher().finished(this);
    12. }

    当任务执行完成后,无论是否有异常,finally代码段总会被执行,也就是会调用Dispatcher的finished函数

    1. void finished(AsyncCall call) {
    2. finished(runningAsyncCalls, call, true);
    3. }

    从上面的代码可以看出,第一个参数传入的是正在运行的异步队列,第三个参数为true,下面再看有是三个参数的finished方法:

    1. private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {

    2. int runningCallsCount;

    3. Runnable idleCallback;

    4. synchronized (this) {

    5. if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");

    6. if (promoteCalls) promoteCalls();

    7. runningCallsCount = runningCallsCount();

    8. idleCallback = this.idleCallback;

    9. }

    10. if (runningCallsCount == 0 && idleCallback != null) {

    11. idleCallback.run();

    12. }

    13. }

    打开源码,发现它将正在运行的任务Call从队列runningAsyncCalls中移除后,获取运行数量判断是否进入了Idle状态,接着执行promoteCalls()函数,下面是promoteCalls()方法:

    1. private void promoteCalls() {

    2. if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.

    3. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    4. for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

    5. AsyncCall call = i.next();

    6. if (runningCallsForHost(call) < maxRequestsPerHost) {

    7. i.remove();

    8. runningAsyncCalls.add(call);

    9. executorService().execute(call);

    10. }

    11. if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.

    12. }

    13. }

    主要就是遍历等待队列,并且需要满足同一主机的请求小于maxRequestsPerHost时,就移到运行队列中并交给线程池运行。就主动的把缓存队列向前走了一步,而没有使用互斥锁等复杂编码

    核心重点getResponseWithInterceptorChain方法

    1. Response getResponseWithInterceptorChain() throws IOException {

    2. // Build a full stack of interceptors.

    3. List<Interceptor> interceptors = new ArrayList<>();

    4. interceptors.addAll(client.interceptors());

    5. interceptors.add(retryAndFollowUpInterceptor);

    6. interceptors.add(new BridgeInterceptor(client.cookieJar()));

    7. interceptors.add(new CacheInterceptor(client.internalCache()));

    8. interceptors.add(new ConnectInterceptor(client));

    9. if (!forWebSocket) {

    10. interceptors.addAll(client.networkInterceptors());

    11. }

    12. interceptors.add(new CallServerInterceptor(forWebSocket));

    13. Interceptor.Chain chain = new RealInterceptorChain(

    14. interceptors, null, null, null, 0, originalRequest);

    15. return chain.proceed(originalRequest);

    16. }

    1)在配置 OkHttpClient 时设置的 interceptors; 2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor; 3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor; 4)负责读取缓存直接返回、更新缓存的 CacheInterceptor; 5)负责和服务器建立连接的 ConnectInterceptor; 6)配置 OkHttpClient 时设置的 networkInterceptors; 7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。

    OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。

    从上述源码得知,不管okhttp有多少拦截器最后都会走,如下方法:

    1. Interceptor.Chain chain = new RealInterceptorChain(
    2. interceptors, null, null, null, 0, originalRequest);
    3. return chain.proceed(originalRequest);

    从方法名字基本可以猜到是干嘛的,调用 chain.proceed(originalRequest); 将request传递进来,从拦截器链里拿到返回结果。那么拦截器Interceptor是干嘛的,Chain是干嘛的呢?继续往下看RealInterceptorChain

    RealInterceptorChain类

    下面是RealInterceptorChain的定义,该类实现了Chain接口,在getResponseWithInterceptorChain调用时好几个参数都传的null。

    1. public final class RealInterceptorChain implements Interceptor.Chain {

    2. public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,

    3. HttpCodec httpCodec, RealConnection connection, int index, Request request) {

    4. this.interceptors = interceptors;

    5. this.connection = connection;

    6. this.streamAllocation = streamAllocation;

    7. this.httpCodec = httpCodec;

    8. this.index = index;

    9. this.request = request;

    10. }

    11. ......

    12. @Override

    13. public Response proceed(Request request) throws IOException {

    14. return proceed(request, streamAllocation, httpCodec, connection);

    15. }

    16. public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,

    17. RealConnection connection) throws IOException {

    18. if (index >= interceptors.size()) throw new AssertionError();

    19. calls++;

    20. ......

    21. // Call the next interceptor in the chain.

    22. RealInterceptorChain next = new RealInterceptorChain(

    23. interceptors, streamAllocation, httpCodec, connection, index + 1, request);

    24. Interceptor interceptor = interceptors.get(index);

    25. Response response = interceptor.intercept(next);

    26. ......

    27. return response;

    28. }

    29. protected abstract void execute();

    30. }

    主要看proceed方法,proceed方法中判断index(此时为0)是否大于或者等于client.interceptors(List )的大小。由于httpStream为null,所以首先创建next拦截器链,主需要把索引置为index+1即可;然后获取第一个拦截器,调用其intercept方法。

    Interceptor 代码如下:

    1. public interface Interceptor {

    2. Response intercept(Chain chain) throws IOException;

    3. interface Chain {

    4. Request request();

    5. Response proceed(Request request) throws IOException;

    6. Connection connection();

    7. }

    8. }

    BridgeInterceptor

    BridgeInterceptor从用户的请求构建网络请求,然后提交给网络,最后从网络响应中提取出用户响应。从最上面的图可以看出,BridgeInterceptor实现了适配的功能。下面是其intercept方法:

    1. public final class BridgeInterceptor implements Interceptor {

    2. ......

    3. @Override

    4. public Response intercept(Chain chain) throws IOException {

    5. Request userRequest = chain.request();

    6. Request.Builder requestBuilder = userRequest.newBuilder();

    7. RequestBody body = userRequest.body();

    8. //如果存在请求主体部分,那么需要添加Content-Type、Content-Length首部

    9. if (body != null) {

    10. MediaType contentType = body.contentType();

    11. if (contentType != null) {

    12. requestBuilder.header("Content-Type", contentType.toString());

    13. }

    14. long contentLength = body.contentLength();

    15. if (contentLength != -1) {

    16. requestBuilder.header("Content-Length", Long.toString(contentLength));

    17. requestBuilder.removeHeader("Transfer-Encoding");

    18. } else {

    19. requestBuilder.header("Transfer-Encoding", "chunked");

    20. requestBuilder.removeHeader("Content-Length");

    21. }

    22. }

    23. if (userRequest.header("Host") == null) {

    24. requestBuilder.header("Host", hostHeader(userRequest.url(), false));

    25. }

    26. if (userRequest.header("Connection") == null) {

    27. requestBuilder.header("Connection", "Keep-Alive");

    28. }

    29. // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing

    30. // the transfer stream.

    31. boolean transparentGzip = false;

    32. if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {

    33. transparentGzip = true;

    34. requestBuilder.header("Accept-Encoding", "gzip");

    35. }

    36. List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());

    37. if (!cookies.isEmpty()) {

    38. requestBuilder.header("Cookie", cookieHeader(cookies));

    39. }

    40. if (userRequest.header("User-Agent") == null) {

    41. requestBuilder.header("User-Agent", Version.userAgent());

    42. }

    43. Response networkResponse = chain.proceed(requestBuilder.build());

    44. HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    45. Response.Builder responseBuilder = networkResponse.newBuilder()

    46. .request(userRequest);

    47. if (transparentGzip

    48. && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))

    49. && HttpHeaders.hasBody(networkResponse)) {

    50. GzipSource responseBody = new GzipSource(networkResponse.body().source());

    51. Headers strippedHeaders = networkResponse.headers().newBuilder()

    52. .removeAll("Content-Encoding")

    53. .removeAll("Content-Length")

    54. .build();

    55. responseBuilder.headers(strippedHeaders);

    56. responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));

    57. }

    58. return responseBuilder.build();

    59. }

    60. /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */

    61. private String cookieHeader(List<Cookie> cookies) {

    62. StringBuilder cookieHeader = new StringBuilder();

    63. for (int i = 0, size = cookies.size(); i < size; i++) {

    64. if (i > 0) {

    65. cookieHeader.append("; ");

    66. }

    67. Cookie cookie = cookies.get(i);

    68. cookieHeader.append(cookie.name()).append('=').append(cookie.value());

    69. }

    70. return cookieHeader.toString();

    71. }

    72. }

    从上面的代码可以看出,首先获取原请求,然后在请求中添加头,比如Host、Connection、Accept-Encoding参数等,然后根据看是否需要填充Cookie,在对原始请求做出处理后,使用chain的procced方法得到响应,接下来对响应做处理得到用户响应,最后返回响应。接下来再看下一个拦截器ConnectInterceptor的处理。

    1. public final class ConnectInterceptor implements Interceptor {

    2. ......

    3. @Override

    4. public Response intercept(Chain chain) throws IOException {

    5. RealInterceptorChain realChain = (RealInterceptorChain) chain;

    6. Request request = realChain.request();

    7. StreamAllocation streamAllocation = realChain.streamAllocation();

    8. // We need the network to satisfy this request. Possibly for validating a conditional GET.

    9. boolean doExtensiveHealthChecks = !request.method().equals("GET");

    10. HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);

    11. RealConnection connection = streamAllocation.connection();

    12. return realChain.proceed(request, streamAllocation, httpCodec, connection);

    13. }

    14. }

    实际上建立连接就是创建了一个 HttpCodec 对象,它利用 Okio 对 Socket 的读写操作进行封装,Okio 以后有机会再进行分析,现在让我们对它们保持一个简单地认识:它对 java.io 和 java.nio 进行了封装,让我们更便捷高效的进行 IO 操作。

    CallServerInterceptor

    CallServerInterceptor是拦截器链中最后一个拦截器,负责将网络请求提交给服务器。它的intercept方法实现如下:

    1. @Override

    2. public Response intercept(Chain chain) throws IOException {

    3. RealInterceptorChain realChain = (RealInterceptorChain) chain;

    4. HttpCodec httpCodec = realChain.httpStream();

    5. StreamAllocation streamAllocation = realChain.streamAllocation();

    6. RealConnection connection = (RealConnection) realChain.connection();

    7. Request request = realChain.request();

    8. long sentRequestMillis = System.currentTimeMillis();

    9. httpCodec.writeRequestHeaders(request);

    10. Response.Builder responseBuilder = null;

    11. if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {

    12. // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100

    13. // Continue" response before transmitting the request body. If we don't get that, return what

    14. // we did get (such as a 4xx response) without ever transmitting the request body.

    15. if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {

    16. httpCodec.flushRequest();

    17. responseBuilder = httpCodec.readResponseHeaders(true);

    18. }

    19. if (responseBuilder == null) {

    20. // Write the request body if the "Expect: 100-continue" expectation was met.

    21. Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());

    22. BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

    23. request.body().writeTo(bufferedRequestBody);

    24. bufferedRequestBody.close();

    25. } else if (!connection.isMultiplexed()) {

    26. // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from

    27. // being reused. Otherwise we're still obligated to transmit the request body to leave the

    28. // connection in a consistent state.

    29. streamAllocation.noNewStreams();

    30. }

    31. }

    32. httpCodec.finishRequest();

    33. if (responseBuilder == null) {

    34. responseBuilder = httpCodec.readResponseHeaders(false);

    35. }

    36. Response response = responseBuilder

    37. .request(request)

    38. .handshake(streamAllocation.connection().handshake())

    39. .sentRequestAtMillis(sentRequestMillis)

    40. .receivedResponseAtMillis(System.currentTimeMillis())

    41. .build();

    42. int code = response.code();

    43. if (forWebSocket && code == 101) {

    44. // Connection is upgrading, but we need to ensure interceptors see a non-null response body.

    45. response = response.newBuilder()

    46. .body(Util.EMPTY_RESPONSE)

    47. .build();

    48. } else {

    49. response = response.newBuilder()

    50. .body(httpCodec.openResponseBody(response))

    51. .build();

    52. }

    53. if ("close".equalsIgnoreCase(response.request().header("Connection"))

    54. || "close".equalsIgnoreCase(response.header("Connection"))) {

    55. streamAllocation.noNewStreams();

    56. }

    57. if ((code == 204 || code == 205) && response.body().contentLength() > 0) {

    58. throw new ProtocolException(

    59. "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());

    60. }

    61. return response;

    62. }

    从上面的代码中可以看出,首先获取HttpStream对象,然后调用writeRequestHeaders方法写入请求的头部,然后判断是否需要写入请求的body部分,最后调用finishRequest()方法将所有数据刷新给底层的Socket,接下来尝试调用readResponseHeaders()方法读取响应的头部,然后再调用openResponseBody()方法得到响应的body部分,最后返回响应。

    最后总结

    OkHttp的底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的),但是OkHttp实现了连接池的概念,即对于同一主机的多个请求,其实可以公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现了连接池的概念。而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。

    本文在开源项目:https://github.com/Android-Alvin/Android-LearningNotes 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中...

    相关文章

      网友评论

        本文标题:Android高工面试被问OKHTTP内核解析,我慌了!

        本文链接:https://www.haomeiwen.com/subject/pvbwvktx.html