OkHttp源码详细解析

优点

OkHttp是一个高效的Http客户端,有如下的特点:

  • 支持HTTP2/SPDY黑科技
  • socket自动选择最好路线,并支持自动重连
  • 拥有自动维护的socket连接池,减少握手次数
  • 拥有队列线程池,轻松写并发
  • 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
  • 基于Headers的缓存策略(不仅可以缓存数据,就连响应头都给缓存了)

概念

源码涉及的主要几个对象

  • Call:对请求的封装,有异步请求和同步请求。
  • Dispatcher:任务调度器
  • Connection:是RealConnection的父类接口,表示对JDK中的物理socket进行了引用计数封装,用来控制socket连接
  • HttpCodec:对Http请求进行编码,对Http响应进行解码,由于Http协议有基于HTTP1.0和- Http2.0的两种情况,Http1Code代表基于Http1.0协议的方式,Http2Code代表基于Http2.0协议的方式。
  • StreamAllocation: 用来控制Connections/Streams的资源分配与释放
  • RouteDatabase:用来保存连接的错误路径,以便能提升连接的效率。
  • RetryAndFollowUpInterceptor 负责失败重试以及重定向的拦截器
  • BridgeInterceptor: 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转为用户友好的响应的
  • CacheInterceptor: 负责读取缓存直接返回、更新缓存
  • ConnectInterceptor: 负责和服务器建立连接的
  • CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据

源码

我们先从使用方法开始慢慢深入源码阶段

 		OkHttpClient mOkHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url("https://blog.csdn.net/dakun012")
                .addHeader("user_agent","00000")
                .build();
          Call call = mOkHttpClient.newCall(request);//1
          call.enqueue(new Callback() {//2
            @Override
            public void onFailure(Call call, IOException e) {

            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

Call

我们来看下注释1处都做了些什么

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

接着调用了RealCall.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;
  }

我们看到实际上返回的是RealCall,RealCall构造方法如下:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

构造方法中只是简单的赋值操作和实例化了RetryAndFollowUpInterceptor拦截器,这个稍后会说,我们返回去看注释2,上面我们看到call其实际类型是RealCall,所以去看下RealCall.enqueue都做了什么

@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));
  }

client.dispatcher()返回的是Dispatcher类型

 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

如果正在运行的异步请求数量小于最大的并发数,且正在运行的每个主机请求数量小于规定的每个主机最大请求数量,那么就把该请求放进正在运行的异步请求队列中,否则就把该请求放进将要执行的异步请求队列中。

Dispatcher

主要参数介绍

 //支持的最大并发请求数量
  private int maxRequests = 64;
 //每个主机的最大请求数量
  private int maxRequestsPerHost = 5;
  //请求线程池
  private @Nullable ExecutorService executorService;
  //将要运行的异步请求队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //正在运行的异步请求队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //正在运行的同步请求队列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

继续看看Dispatcher的构造方法和executorService方法,如下:

public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

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

我们看到线程池是可以自定义的,只要在构造OkHttpClient 时传入自定义Dispatcher即可
我们重点看下executorService

Dispatcher初始化了一个线程池,核心线程的数量为0,最大的线程数量为Integer.MAX_VALUE,空闲线程存在的最大时间为60秒,这个线程类似于CacheThreadPool,比较适合执行大量的耗时比较少的任务。同时我们Dispatcher也可以来设置自己线程池。

了解了这些概念后,我们回到executorService().execute(call)方法继续看

call的类型是AsyncCall继承自NamedRunnable,NamedRunnable继承自Runnable,上面的execute(call)方法自然是执行的Runnable.run()方法,NamedRunnable实现了run方法

  @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();

所以最终会调用AsyncCall.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);
      }
    }

RealCall通过执行getResponseWithInterceptorChain()返回Response,如果请求被取消或发生异常则在进行OnFailue回调,如果请求成功则进行onResponse的回调。

这里要注意两点:

  • 请求如果被取消,其回调实在onFailue中进行回调的
  • enqueue方法的回调是在子线程中完成的

拦截器

AsyncCall是RealCall 内部类,AsyncCall调用的是RealCall.getResponseWithInterceptorChain(),那么RealCall 的getResponseWithInterceptorChain方法中究竟干了些什么呢,它是如何返回Response的呢?

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);
  }

interceptors 依次添加了

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

在 return chain.proceed(originalRequest),中开启链式调用

RealInterceptorChain的proceed方法源码如下:

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
	//省略部分代码
	
    // 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);
    
   //省略部分代码
    return response;
  }

这段猛一看有点头大,其实这个有点类似迭代调用的意思,盗个别人的图看下哈哈~
在这里插入图片描述
首先了解下拦截器,拦截器是一种能够监控、重写,重试调用的机制。通常情况下,拦截器用来添加、移除、转换请求和响应的头部信息。比如将域名替换为IP地址,在请求头中移除添加host属性;也可以添加我们应用中的一些公共参数,比如设备id、版本号,等等。

拦截器的基本代码结构如下:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;
  interface Chain {
    Request request();
    Response proceed(Request request) throws IOException;
}
}

具体流程我们着重看下如下两个拦截器来梳理我们的思路

  • BridgeInterceptor
  • CacheInterceptor

我们假设第一个拦截器是BridgeInterceptor, Response response = interceptor.intercept(next);调用的就是BridgeInterceptor.intercept方法

@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
	//省略......操作原Request 
    Response networkResponse = chain.proceed(requestBuilder.build());
    
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
     //省略......操作得到的Response
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    return responseBuilder.build();
  }

我们看到在对原Request 进行一系列的修改操作后,又调用了chain.proceed(requestBuilder.build()),上面我们看了相应源码,其结果会再次调用下一个拦截器,如此一致循环调用,直到最后一个CallServerInterceptor拦截器的intercept调用后,得到Response 开始一级一级返回给上层拦截器

CallServerInterceptor.intercept源码如下:

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
        // from being reused. Otherwise we're still obligated to transmit the request body to
        // leave the connection in a consistent state.
        streamAllocation.noNewStreams();
      }
    }

    httpCodec.finishRequest();

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

    int code = response.code();
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

流程总结如下

我们Request 是经过拦截器一层层修改后发送给服务端的(a->b->c)
我们得到的response,也是拦截器一层层修改后返回给我们的(c->b->a)

拦截器详解

未完待续

本文章参考其他文章,加上自己的理解和认为难点的地方修改,如有侵权请联系我修改,再次感谢

参考文章: https://www.jianshu.com/p/5b7ccc7e5bb7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值