OkHttp源码分析之(一)请求流程

0.前言

OkHttp,相信大家都熟悉,就连retrofit内部也是基于它实现的,是一个重量级的网略请求框架。其支持同步请求和异步请求,核心的东西是内部维护了一个拦截器和线程池,用于存放网略请求和拦截,其内部实现的方法,真的是妙不可言,其中的妙处何在?让我们来一步一步了解。
本系列共两篇文章:
OkHttp源码分析之(一)请求流程
OkHttp源码解析之(二)任务调度——Dispatcher和拦截器

1.使用

(1)okhttp的使用介绍还是要有的,首先当然是先导入依赖包:

implementation 'com.squareup.okhttp3:okhttp:3.6.0'

(2)使用三步曲

  • OkHttpClient:
 val client = OkHttpClient.Builder()
            .readTimeout(READ_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置读取超时时间
            .writeTimeout(WRITE_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置写的超时时间
            .connectTimeout(CONNECT_TIMEOUT.toLong(), TimeUnit.SECONDS)//设置连接超时时间
            .build()

这里简单的只是设置了下超时和读写时间

  • Request
 val request = Request.Builder()
                .post(body)
                .url(url)
                .build()

使用request构造我们的请求,将请求参数传递进去。

  • newCall
 client.newCall(request).enqueue(myCallBack)//异步请求,无阻塞
 client.newCall(request).execute();//同步请求,阻塞进程

就是这么简单,那么这几个步骤究竟干了什么呢?为什么一定要这几个步骤呢?我们不是要请求网络吗?我可以肯定的告诉你,第一步设置好连接状态相关的参数,第二步写好请求内容,第三步发起请求。接下来从源码角度上看看这三步到底做了什么。

2.源码解析

鉴于源码比较长,为避免乏味,我尽量在源码里面采用注释说明的方法来解析,然后再重点强调一下。
(1)OkHttpClient
首先是OkHttpClient,一般生成client是使用build方法,先来看看Builder()这厮干了什么

  • Builder
public Builder() {
      dispatcher = new Dispatcher();//初始化任务调度器——OkHttp的核心之一
      protocols = DEFAULT_PROTOCOLS;//协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();//线程池,核心之一
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

可以看到,Builder方法里面对线程池和调度器、读写超时时间等进行了初始化,已备后续使用,Builder是内部定义的一个类,包含readTimeout()、writeTimeout等这些我们常用的方法,方法本身又返回Builder本身,所以可以连续几个方法调用下去。这两个方法的源码如下:

public Builder readTimeout(long timeout, TimeUnit unit) {
      readTimeout = checkDuration("timeout", timeout, unit);
      return this;
    }

public Builder writeTimeout(long timeout, TimeUnit unit) {
      writeTimeout = checkDuration("timeout", timeout, unit);
      return this;
    }

没错,使用了建造者模式!
返回Builder之后,又去调用了build方法创建OkHttpClient对象:

  • build
 public OkHttpClient build() {
      return new OkHttpClient(this);
    }

生成OkHttpClient对象,又把本身传递过去,看看OkHttpClient的构造方法都做了什么

final Dispatcher dispatcher;
  final @Nullable Proxy proxy;
  final List<Protocol> protocols;
  final List<ConnectionSpec> connectionSpecs;
  final List<Interceptor> interceptors;
  final List<Interceptor> networkInterceptors;
  final EventListener.Factory eventListenerFactory;
  final ProxySelector proxySelector;
  final CookieJar cookieJar;
  final @Nullable Cache cache;
  final @Nullable InternalCache internalCache;
  final SocketFactory socketFactory;
  final SSLSocketFactory sslSocketFactory;
  final CertificateChainCleaner certificateChainCleaner;
  final HostnameVerifier hostnameVerifier;
  final CertificatePinner certificatePinner;
  final Authenticator proxyAuthenticator;
  final Authenticator authenticator;
  final ConnectionPool connectionPool;
  final Dns dns;
  final boolean followSslRedirects;
  final boolean followRedirects;
  final boolean retryOnConnectionFailure;
  final int callTimeout;
  final int connectTimeout;
  final int readTimeout;
  final int writeTimeout;
  final int pingInterval;

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

OkHttpClient(Builder builder) {
  //从builder里面取出参数进行赋值,值为刚才初始化时的值或返回Builder之后调用readTimeout等方法赋值之后的值
    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.eventListenerFactory = builder.eventListenerFactory;
    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 = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    if (sslSocketFactory != null) {
      Platform.get().configureSslSocketFactory(sslSocketFactory);
    }

    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.callTimeout = builder.callTimeout;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;

    if (interceptors.contains(null)) {
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }

代码不多,主要就是对OkHttpClient需要用到的值进行初始化。这样一来,client就获取到了。
(2)Request
获取到client之后,接下来第二步就是获取request。request,请求,顾名思义,即网络请求的内容(怎么说呢,如果有javaWeb基础的就好理解了,相当于网络请求中的请求内容),看看它做了什么。

  • Builder
    和client类似,首先也是用Builder()获取Builder:
 public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

代码只有简简单单的四行,设置默认请求为get请求(你可以在获取Builder之后调用post()方法更改为post请求)。
然后是build()方法

  • build()
public Request build() {
      if (url == null) throw new IllegalStateException("url == null");//空异常
      return new Request(this);
    }

也是传递自身为参数,返回Request对象:

 final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Map<Class<?>, Object> tags;

  private volatile @Nullable CacheControl cacheControl; // Lazily initialized.

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }

同样,也是初始化。
由此可知,这两步主要就是为了初始化参数值,为以后做准备。

(3)newCall
client里面的newCall方法,才是真正执行网络请求的方法

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

这厮有点懒,把任务丢给RealCall去做。看看RealCall又干了什么:

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);//生成RealCall 对象
    call.eventListener = client.eventListenerFactory().create(call);//赋值事件监听
    return call;
  }

如注释所言,其中 client.eventListenerFactory()的值在前两步初始化client时已经附值,这里仅仅是调用它的create方法而已。
方法最后调用的是RealCall的对象引用,然后就可以用它进行网络请求啦!
这里,分为两种情况:一种是同步请求,另一种是异步请求。
这里分别对应这两种情况进行源码解析:

同步请求execute()
先来看看同步请求,刚才说了,client比较懒,把事情丢给RealCall去做,所以这里,我们要看RealCall里面的execute()方法:

 @Override public Response execute() throws IOException {
    //锁住
    synchronized (this) {
    //避免重复
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

    captureCallStackTrace();    //处理异常
    timeout.enter();
    eventListener.callStart(this);//开启监听(每当调用execute和enqueue方法)
    try {
      client.dispatcher().executed(this);//调用dispatcher方法将同步请求添加到同步请求队列中
      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);//回收同步请求(从同步请求队列中移除)
    }
  }

//Dispatcher类中
/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);//添加到同步请求队列中
  }

dispatcher内维护了同步请求队列、异步请求队列和异步请求等待队列,getResponseWithInterceptorChain这里稍后再详细深入解析,这里先了解下大概流程。方法最后调用 client.dispatcher().finished(this)回收同步请求,:

 /** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call);
  }

 private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");//移除同步请求,如果失败抛异常
      idleCallback = this.idleCallback;
    }

    boolean isRunning = promoteAndExecute();//返回是否还有请求

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

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      //异步请求才会执行的代码
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
        //数量不能超过隔值
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;//即是否还有未处理的同步请求
    }
    //异步请求执行的代码
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

就是这么简单,总结起来就干了两件事:保存同步请求、移除同步请求
接下来看看它的异步请求
异步请求enqueue(callBack)
同样,异步请求也在RealCall类里面,先来看看它的代码:

@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));//将异步请求添加至队列中,AsyncCall为内部定义的runnable类
  }

与execute方法类似,最后调用Dispatcher方法将请求添加至异步请求队列中,代码如下:

 void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);//加入队列
    }
    promoteAndExecute();
  }

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();//用于放置正在处理的异步请求
    boolean isRunning;
    synchronized (this) {
      //异步请求执行的代码
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        executableCalls.add(asyncCall);//入队
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;//即是否还有正在执行的异步请求
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());//具体执行请求的地方
    }

    return isRunning;
  }

 //executorService为线程池,定义了线程池大小以及存活时间
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;
  }

//贴出ThreadPoolExecutor的构造方法,对数值进行初始化
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

方法执行与execute方法类似,最后是调用AsyncCall类的executeOn方法执行,AsyncCall是个子线程,网略请求放在了子线程,即异步请求。
总结起来,enqueue几件事:加入等待执行的队列中、加入可执行的队列中、执行请求

接下来看看具体的网略请求:

  • AsyncCall
 final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;//responseCallback用于回调请求结果,即我们一开始传进来的Callback ,是不是很熟悉?
    }

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

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    /**
     * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);//执行runnable
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        //失败回调
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!,同样是回收
        }
      }
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
        Response response = getResponseWithInterceptorChain();//核心之一,拦截器链
         //判断retryAndFollowUpInterceptor——重定向重置拦截器释放取消了
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));//如果取消了请求,回调请求失败
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);//请求成功,就调用response方法
        }
      } catch (IOException e) {
        e = timeoutExit(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);//移除请求
      }
    }
  }

有关拦截器的分析,由于篇幅关系,这里不做解释,详情请看本系列文章:OkHttp源码解析之(二)任务调度——Dispatcher和拦截器
可以看到,AsyncCall做的事情也不多:执行请求、回调结果

总结

通过以上分析,我们知道OkHttp的网略请求过程遵循“三步曲”,请求又分为同步请求和异步请求,同步请求返回一个Response对象,异步请求无返回,结果通过CallBack回调返回。由于篇幅关系,本文侧重于讲解OkHttp的执行流程,让大家有个清晰的流程,那么接下来,是时候讲讲OkHttp的核心:Dispatcher和拦截器链

流程图还是要有的:


OkHttp.png

图有点长,包含了主要的步骤,慢慢看吧~~

原创文章,转载请附上https://www.jianshu.com/p/d1fe452ba6ff

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容