okhttp3x源码分析-请求流程

本文深入分析了OkHttp3的3.11.0版本请求流程,包括同步和异步请求的创建及执行过程。通过构建OkhttpClient、Request和Call,详细解读了RealCall内部的execute()和enqueue()方法。同步请求利用Dispatcher任务队列管理,不涉及线程池;异步请求则通过Dispatcher的无界线程池处理,当请求完成会回调到AsyncCall的execute()方法。请求过程中涉及拦截器链和Dispatcher的重要角色。

本文基于okhttp3的3.11.0版本进行请求流程的分析:

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

在这里插入图片描述

okhttp3的基本请求流程如上图所示,首先构建了网络请求所需的4个类(OkhttpClient,OkhttpClient.Builder,Request,Call),然后通过call.execute()或者call.enqueue(callback)来发起同步请求或者异步请求,下面我们来具体分析一下这两种方式如何进行网络请求的。

okhttp3发起请求的基本方式

1:创建OkhttpClient.Builder与OkhttpClient

  OkHttpClient.Builder builder = new OkHttpClient().newBuilder()
                .connectTimeout(20,TimeUnit.SECONDS)
                .writeTimeout(20,TimeUnit.SECONDS)
                .readTimeout(20,TimeUnit.SECONDS);
    OkHttpClient okHttpClient = builder.build();

2:构建Request

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

3:获取Call对象进行同步请求或者异步请求

 Call call = okHttpClient.newCall(request);
1:call.execute();//同步请求
2:call.enqueue(callback);//异步请求   callback是okhttp3里面的Callback的实例对象

以上你okhttp3的简略请求步骤,下面我们通过跟踪源码的方式来具体分析一下请求的过程。
1:在获取Call对象是通过OkhttpClient里面的newCall方法进行构建的,newCall方法代码如下

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

在newCall方法内部只调用了RealCall.newRealCall(this, request, false /* for web socket */)方法,进入RealCall中的newRealCall()方法如下:

RealCall call = new RealCall(client, originalRequest, forWebSocket);
 call.eventListener = client.eventListenerFactory().create(call);
    return call;

在这个方法内部,直接创建了一个RealCall对象,然后返回这个RealCall对象,而且RealCall是Call的子类(final class RealCall implements Call),它实现了Call类里面的方法,所以我们发送请求无论是调用execute()还是enqueue(),实际上都是调用了RealCall内部的方法。

//RealCall类内部的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);//注释③
    }
  }

//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));//注释④
  }

从RealCall类中的execute()与enqueue()方法中我们主要关注注释①②③④这四个点就基本上可以知道okhttp3.0网络请求的基本流程了,下面我们大概解释一下上面注释的方法。

(1)注释①中client.dispatcher().executed(this) 这里只是把同步请求的call加入了Dispatcher的任务队列管理中。
(2)注释②中是调用了okhttp中的拦截器链,经过一系列的操作,最终返回响应结果Response。
(3)注释③中是无论请求的结果如何,调用了Dispatcher的finished方法,将当前的请求移出队列。
(4)注释④是异步请求利用了Dispatcher的线程池来处理请求。

从RealCall类可以看出在RealCall内部的enqueue方法和execute方法中,都是通过OkHttpClient的任务调度器Dispatcher来完成。下面我们先来看看Dispatcher。

okhttp3的任务调度器-Dispatcher

通过client.dispatcher.execute(this)方法,我们点进去,可以看到在OkhttpClient类中的dispatcher()方法的具体内容如下:

  public Dispatcher dispatcher() {
    return dispatcher;
  }

我们发现client.dispatcher()返回的是dispatcher对象,那么这个Dispatcher是何时创建的?我们查看OkhttpClient的构造方法如下:

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

直接返回了一个new Builder(),我们继续跟踪代码,进入Builder()构造方法如下:

 public Builder() {
      dispatcher = new Dispatcher();
   	  ......
 }

在Builder()方法中直接new 出来了一个Dispatcher对象,但是我们发现我们上面的client.dispatcher是Okhttpclient中的Dispatcher对象,我们这里跟踪到new 出来的Dispatcher对象是OkhttpClient中内部类Builder中的Dispatcher,我们继续看OkhttpClient的另外一个构造方法,如下:

 OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    ......
 }

在这个构造方法中把Builder作为参数传进来,然后赋值给OkhttpClient中的Dispatcher,然后这个构造方法什么时候调用的呢?在我们构建OkhttpClient的示例如下:

 OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

我们点进去在内部类中Builder中的build()方法,在这个build方法如下:

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

在这个方法中直接返回了一个 new OkhttpClient(this)方法,这里的this就是当前的Builder类,也就是直接调用了OkHttpClient(Builder builder)构造方法,然后把Builder中的Dispatcher对象赋值给Okhttp中的Dispatcher。

现在我们弄清楚了OkhttpClient中的Dispatcher对象是怎么赋值的,然后我们看看Dispatcher内部做了一些什么操作。

首先我们要知道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类中的构造方法如下所示:

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

(1)Dispatcher中的默认构造方法是个空实现,线程池的加载方式采用的是懒加载,也就是在异步请求第一次调用的时候初始化,而我们上面构建Dispatcher实例就是调用了public Dispatcher() {} 这个构造方法。

(2)Dispatcher采用的线程池是无边界限制的线程池也就是没有核心线程,非核心线程数很大,也就是说可以无限的创建线程,比较适合执行大量的耗时较少的任务,线程池处于闲置状态的时候,线程池中的线程都会被销毁,这个时候该线程池几乎是不占用任何系统资源的。

下面我们分析一下ThreadPoolExecutor线程池中的参数的具体含义。

1、0:核心线程数量,保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
2、Integer.MAX_VALUE:表示线程池可以容纳最大线程数量
3、TimeUnit.SECOND:当线程池中的线程数量大于核心线程时,空闲的线程就会等待60s才会被终止,如果小于,则会立刻停止。
4、new SynchronousQueue<Runnable>():线程等待队列。同步队列,按序排队,先来先服务
5、Util.threadFactory("OkHttp Dispatcher", false):线程工厂,直接创建一个名为OkHttp Dispatcher的非守护线程。

(1)SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素,使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界(Integer.MAX_VALUE),避免线程拒绝执行操作。

(2)在OKHttp中,创建了一个阀值是Integer.MAX_VALUE的线程池,它不保留任何最小线程,随时创建更多的线程数,而且如果线程空闲后,只能多活60秒。所以也就说如果收到10个并发请求,线程池会创建10个线程,当完成后的60秒后会自动关闭所有10个线程。他这样设计成不设上限的线程,以保证I/O任务中高阻塞低占用的过程,不会长时间卡在阻塞上。

下面我们来看看okhttp中的同步请求或者异步请求流程具体的实现细节。

同步请求流程分析

同步请求发起方式,okHttpClient.newCall(request).execute(),我们点进去execute()方法,我们就进入到了RealCall中的execute方法(具体为什么会是进入RealCall中的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);//注释④
    }
  }

如上所示我们具体分析一下上面打了注释的代码,在注释①中它主要是判断这个Call是否被执行过,可以看出每一个Call对象是只能使用一次,

现在我们来看看注释② client.dispatcher().executed(this);点进去如下,

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

它这里只执行Dispacther的executed方法,将任务添加到同步任务队列中,没有使用Dispatcher中的线程池。我们看注释③,它执行了getResponseWithInterceptorChain()方法,进入拦截器链流程(具体流程下一篇分析),然后进行请求,获取Response,并且返回Response。到最后我们来看注释④,它调用了client.dispatcher().finished(this),我们点进去Dispatcher中的finished方法中,如下:

void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

在这个方法中直接调用了 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();
    }
  }

在注释①中从正在运行的队列(runningSyncCalls)直接移除了本次执行的call。至此,同步请求操作分析完。

同步请求总结

综合流程分析,同步请求只是利用了Dispatcher的任务队列管理,没有利用Dispatcher的线程池,然后通过拦截器链进行请求操作返回响应结果,最后不管结果如何都会调用Dispather里的finished方法,对于本次请求进行移除操作。

异步请求流程分析

异步请求发起方式,okHttpClient.newCall(request).enqueue(callback),我们进入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));//注释②
  }

注释①中它主要是判断这个Call是否被执行过,可以看出每一个Call对象是只能使用一次。

注释②中,我们点进Dispatcher中的enqueue方法如下:

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

(1)从上面代码中可以看出,如果当前正在执行的call的数量小于maxRequests(64),或者该call的Host上的call小于maxRequestsPerHos(5),则加入runningAsyncCalls队列并执行。反之,加入readyAsyncCalls队列排队等待。

(2)在这个方法中,我们可以看到, executorService().execute(call);就是把传进来的call对象加入了线程池中进行执行,当我们在线程池中任务执行,我们的结果肯定会回调到Call接口的具体实现类中,但是在Dispatcher这个类中我们没有看到call的具体实现类,我们重新再看创建一个异步请求的代码,client.dispatcher().enqueue(new AsyncCall(responseCallback));原来以参数传进Dispatcher类中的enqueue方法中的call是直接new AsyncCall,那么现在逻辑很明了了,当在线程池执行任务,我们获取的结果就会回调到AsyncCall(RealCall的内部类)的execute()方法中,为什么会回调在AsyncCall的execute()方法中呢?我们来看一下AsyncCall中是如何操作的,如下所示:

final class RealCall implements Call {
 	......省略代码
  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
	......省略代码
  @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);
      }
    }
  }
	......省略代码
}

如上所示,我们可以看出:
(1)AsyncCall是RealCall类的内部类,并且继承了NamedRunnable。

(2)在AsyncCall中的构造方法中直接把传进来的responseCallback赋值给AsyncCall类中的变量Callback。

(3)实现了NamedRunnable中的execute()方法。

我们再点进去看一下NamedRunnable类中的具体逻辑,如下:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

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

(1)NamedRunnable是一个抽象类并且实现了Runnable。

(2)在NamedRunnable类中定义了一个抽象方法execute()在run()方法中调用,而execute()方法的具体逻辑就是在RealCall中的内部类AsyncCall中的execute()方法执行。所以在线程池中执行的任务最终回调会在AsyncCall类中的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);//注释④
      }
    }

(1)注释①执行了getResponseWithInterceptorChain()方法,进入拦截器链流程(具体流程下一篇分析),然后进行请求,获取Response。

(2)注释②当次请求如果是canceled的状态,然后就会把这个状态回调到callback的具体实现类中的onFailure方法中。

(3)注释③把当次请求获取的response回调到callback的具体实现类中的onResponse方法中。

(4)注释④是移除当次请求的AsyncCall。

我们再进入当异步请求执行的Dispatcher类中的finished方法,看在异步请求下是怎么移除请求的,如下所示:

 void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

我们可以看到,在finished方法中的第三个参数是true异同与同步请求的false。我们再进去finished方法,如下所示:

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

在注释①中,是从正在运行的队列(runningSyncCalls)直接移除了本次执行的call,因为promoteCalls现在是true,所以执行了注释②中的promoteCalls()方法。我们点进去promoteCalls()方法,如下:

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // 注释①
    if (readyAsyncCalls.isEmpty()) return; //注释②
	
     //注释③
    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; // 注释④
    }
  }

(1)注释①如果此时并发的数量还是大于maxRequests=64,则直接返回。

(2)注释②如果此时没有等待的任务队列(readyAsyncCalls),则直接返回。

(3)注释③遍历等待队列(readyAsyncCalls)中的call,如果同一个host小于5个请求则从等待队列(readyAsyncCalls)中移除,然后加入运行的任务队列(runningAsyncCalls)中,最后线程池(executorService)执行execute()方法。

(4)注释④当正在运行的任务队列runningAsyncCalls>=64的时候,则返回。

异步请求总结

综合流程分析,异步请求利用了Dispatcher的线程池来处理请求,当我们发起一个异步请求时,首先会将我们的请求包装成一个AsyncCall,如果当前还能可以执行异步任务,则入队(runningAsyncCalls),并立即执行,否则加入等待队列(readyAsyncCalls)。当一个请求执行完毕后,会调用promoteCalls()方法,来把等待队列(readyAsyncCalls)中的AsncCall移出来,并加入到执行队列(runningAsyncCalls),并开始执行。然后在当前线程中去执行getResponseWithInterceptorChain(),直接获取当前返回的数据Response。

分析到这里,okhttp的请求流程就分析完毕了,下一篇,我们分析一下okhttp中的拦截器链是怎么运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值