Android面试相关 - Okhttp3源码分析

在这里插入图片描述

implementation ‘com.squareup.okhttp3:okhttp:3.11.0’

背景

之前的底层网络库基本就是Apache HttpClient和HttpURLConnection。由于HttClient比较难用,官方在Android2.3以后就不建议用了,并且在Android5.0以后废弃了HttpClient,在Android6.0更是删除了HttpClient。
HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序,但是在Android 2.2版本之前存在一些bug,所以官方建议在Android2.3以后替代HttpClient,Volley就是按版本分区使用这两个网络库。
然而随着开源届扛把子Square的崛起,OkHttp的开源,这两个网络库只能被淹没在历史洪流中。Android4.4以后HttpURLConnection的底层已经替换成OkHttp实现。OkHttp配合同样是Square开源的Retrofit,网络请求变得更简便,功能更强大。

介绍

OkHttp是一个现代,快速,高效的网络库,OkHttp 库的设计和实现的首要目标是高效。
1.支持 HTTP/2和SPDY,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接;
2.如果 HTTP/2和SPDY不可用,OkHttp会使用连接池来复用连接以提高效率。
3.支持Gzip降低传输内容的大小
4.支持Http缓存
以上均是摘抄别人的东西

简单使用

   //创建OkHttpClient 对象
   OkHttpClient client = new OkHttpClient();
   //创建request
   Request request = new Request.Builder()
                 .url("")
                 .build();
   //创建请求对象
   Call call = okHttpClient.newCall(request);
   //发起同步请求
   try {
       Response execute = call.execute();
   } catch (Exception e) {
       e.printStackTrace();
   }
   //发起异步请求
   call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {

      }

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

      }
 });

源码分析

第一步:创建OkHttpClient对象

   OkHttpClient client = new OkHttpClient();

    //另一种方法是通过Builder创建
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    //可以设置一些builder,如设置连接超时时间
    builder.connectTimeout(5000, TimeUnit.SECONDS);
    //初始化OkhttpClient完成
    OkHttpClient okHttpClient = builder.build();

首先我们点击创建的 OkHttpClient 对象进去源码是这样的:

  public OkHttpClient() {
    this(new Builder());
  }

然后是走了有参构造:

  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;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }

可以看到有很多常量,这里使用了建造者模式,所以这些常量可以通过 build() 进行配置。如果不进行配置则使用无参构造中传进来的默认配置,每个常量的意思具体如下:

    public Builder() {
      // 分发器Dispatcher对象
      dispatcher = new Dispatcher(); 
      // 协议集合,默认协议集合包括HTTP2,HTTP_1_1 
      protocols = DEFAULT_PROTOCOLS;    
      // 传输层版本和连接协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      // 代理选择器
      proxySelector = ProxySelector.getDefault();
      // Cookie瓶,为HTTP的cookies提供策略和持久化
      cookieJar = CookieJar.NO_COOKIES;
      // socket工厂类
      socketFactory = SocketFactory.getDefault();
      // 主机名字确认
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      // 证书链
      certificatePinner = CertificatePinner.DEFAULT;
      // 代理服务器身份验证
      proxyAuthenticator = Authenticator.NONE;
      // 源服务器身份验证
      authenticator = Authenticator.NONE;
      // 连接池
      connectionPool = new ConnectionPool();
      // 域名
      dns = Dns.SYSTEM;
      // 是否遵循 ssl 重定向
      followSslRedirects = true;
      // 是否遵循重定向
      followRedirects = true;
      // 连接失败的时候是否重试  
      retryOnConnectionFailure = true;
      // 连接超时
      connectTimeout = 10_000;
      // 读取超时
      readTimeout = 10_000;
     // 写入超时
      writeTimeout = 10_000;
      // HTTP / 2 和 Web 套接字 ping 之间的时间间隔
      pingInterval = 0;
    }

第二步:创建Request对象

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

可以看到,这里同样使用了建造者模式,我们点击 Request 进去看看:

  final HttpUrl url;
  final String method;
  final Headers headers;
  final RequestBody body;
  final Object tag;

Request只是用来设置一些请求链接(url)、请求方法(method)、请求头(headers)、请求体(body)、标签(tag,可作为取消请求的标记)。

第三步:创建请求对象Call

   Call call = okHttpClient.newCall(request);

首先我们点进newCall()方法里去看一下:

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

发现调用的的是RealCall类里面的newRealCall()方法,并传入了了okttpclient和request。
再跟进到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 是 Call 的实现类,Call 定义了请求相关的操作,例如同步异步、取消请求等方法。所以后续的请求相关操作基本都是在调用 Call 定义的方法,而这些方法真正的执行是它的实现类 RealCall。

最后看看 RealCall 的构造函数,该函数是比较简单的,只是赋值一些常量,然后创建了重试与重定向拦截器(RetryAndFollowUpInterceptor)(这个后面会讲):

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

到这之后我们就可以去发起请求了,像上面的简单使用,可以发起同步请求execute(),或者发起异步请求enqueue(),下面首先说一下同步请求,比较简单,之后再说异步请求。

第四步:同步请求

 Response execute = call.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);
    }
  }

首先我们先看一下刚进入方法的这一个判断,尔值executed表示一个okhttp请求只能运行执行一次

 synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

接着往下看真正调用请求的是这个方法,分发器的excuted()方法:

      client.dispatcher().executed(this);

点进去会发现调的是Dispatcher类executed()方法,先来看一下Dispatcher,主要是有以下里面的东西:

  //最大请求数是64
  private int maxRequests = 64;
  //最大主机连接数是5
  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类,继续来看executed()方法:

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

里面的add()方法是将同步请求任务添加到同步请求队列中。

下面就到了最重要的一步,获取请求结果的方法:

      Response result = getResponseWithInterceptorChain();

进入到方法里去看一下:

  Response getResponseWithInterceptorChain() throws IOException {
    // 创建一个拦截器集合,
    List<Interceptor> interceptors = new ArrayList<>();
    //添加用户自定义的interceptors
    interceptors.addAll(client.interceptors());
    //添加retryAndFollowUpInterceptor拦截器,主要负责请求失败重试
    interceptors.add(retryAndFollowUpInterceptor);
    /** 
     *    添加桥拦截器,设置请求头内容类型、长度、编码。添加cookie,设置其他报头,如User-Agent,Host,Keep-alive等。其中 
     *    Keep-Alive是实现多路复用的必要步骤设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
     */    
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
     // 添加缓存拦截器,主要负责cache管理
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 添加连接拦截器,负责获取连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
     // 添加用户自定义的网络拦截器
      interceptors.addAll(client.networkInterceptors());
    }
    // 添加服务器请求拦截器,负责向服务器发起真正的网络请求,并接收到服务器响应以后返回请求结果。
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //  构建责任链,通过拦截器构造Interceptor.Chain,其真正实现是RealInterceptorChain
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    // 处理责任链中的拦截器,通过拦截器链的proceed()方法获取请求结果
    return chain.proceed(originalRequest);
  }

可以看到,这里用到了很多拦截器,将这些拦截器构建成一条责任链,然后再一个个处理。这里用到了责任链模式,每个拦截器负责相应的功能,上一个拦截器完成会传给下一个拦截器,直到最后一个拦截器执行完再一层层向上返回 Response。
接下来点进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;
  }

其他的一些判断就不看了,主要看一下这个:

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

这里会构建一个新的责任链,然后把责任链的索引加 1(为了下次从拦截器集合中取出下一个拦截器),接着从拦截器集合中取出当前拦截器并调用 intercept() 方法,这样如果这个拦截器可以完成任务会马上返回 Response,否则会在 intercept() 方法中继续处理责任链,因为该 intercept() 方法中会继续调用责任链的 proceed() 方法。通过责任链模式,循环调用,最终获取请求结果。

获取结果后还会去调用finished()方法,是在在finally中执行的:

      client.dispatcher().finished(this);

点进去是这样的:

  void finished(RealCall call) {
    //注意传递的是哪个队列,注意boolen值,一个是同步用到的,一个是异步用到的
    finished(runningSyncCalls, call, false); 
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

calls.remove(call) 只是把当前 RealCall 从正在运行的同步请求队列中移除了,说明请求已经完成了。
到这里同步请求的分析就已经完了,下面分析一下异步请求的流程。

第五步:异步请求

 call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

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

            }
        });

进到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().enqueue(new AsyncCall(responseCallback));

下面我们点进去这个方法里面看一下:

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

这个方法大概就是这样,正在运行的异步请求队列数“ 小于 ”最大并发请求数“,并且 ”每个主机正在运行的请求数“ 小于 ”每个主机最大请求数“,则将当前请求继续加入 ”正在运行的异步请求队列“ 并在线程池中执行,否则将当前请求加入 ”准备中的异步请求队列“。
我们看到线程池中还传了一个 AsyncCall 进去,点击进去看看:

 final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        //这一块和同步请求的一样
        Response response = getResponseWithInterceptorChain();
       /**
          *  判断重定向和重试拦截器是否取消,如果取消,调用responseCallback的onFailure回调,responseCallback就是我们通过          
          *  enqueue方法传入的Callback对象。如果没取消,调用responseCallback的onResponse回调。
          */
        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);
      }
    }
  }

我们发现AsyncCall继承自NamedRunnable,而NamedRunnable实现了Runnable,并且是在NamedRunnable执行了run方法,在这个run()方法中执行了excute()方法也就是AsyncCall中的那个excute()方法。这个excute()方法就不多说了,就是获取请求结果,相信大家一定能看的懂。最后讲一下这个方法:

        client.dispatcher().finished(this);

点进去看一下和同步请求的有什么不同:

 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

finished方法内部会将本次的异步请求RealCall从正在请求的异步请求队列中移除,由于promoteCalls传入的是true,接着调用promoteCalls()方法。
进到promoteCalls()方法里看一下:

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

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

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

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

这个里面做的就是,循环从readyAsyncCalls中取出任务,然后将该任务放到runningAsyncCalls中,最后再调用调用executorService().execute(call)执行任务。

到这里就结束了,历时一周,经过东拼西凑终于是写完了这篇帖子,发现问题的,大家可以留在评论里指出,我在进行修改。

最后,感觉对大家有用的,可以点一下关注,会不定时分享一些安卓相关的知识点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值