okhttp源码分析第一篇:执行流程

费话不多说,先上代码

final OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
              .url("https://www.baidu.com")
              .build();
Response response = okHttpClient.newCall(request).execute();

这是一个同步get请求的代码。代码包含了三个步骤:
1、创建okhttpClient;
2、创建所需的请求信息(request对象);
3、通过okhttpClient来执行该请求。

下面就对以上三个步骤的源码进行分析。

创建okhttpClient

OkHttpClient通过建造者模式来创建对象:使用静态内部类Builder对象的参数来为OkHttpClient对象自身的参数进行赋值。

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

OkHttpClient(Builder builder) {
    //通过builder中参数为自身的参数赋值
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    ...
  }

OkHttpClient.Builder对象的构造器中会对参数赋初始值,源码如下:

public Builder() {
      dispatcher = new Dispatcher();  //请求调度器,通过线程池来对异步请求进行管理
      protocols = DEFAULT_PROTOCOLS;//http协议,默认支持TTP2.X和HTTP1.X
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//对socket连接进行配置的类,包含URL地址,TLS版本和一些加密信息
      eventListenerFactory = EventListener.factory(EventListener.NONE);//请求监听器
      proxySelector = ProxySelector.getDefault();
      ...
      connectionPool = new ConnectionPool();//默认创建一个连接池,多个OkHttpClient实例会导致创建多个线程池对象。
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;//默认支持重定向
      retryOnConnectionFailure = true;//默认支持失败重连
      callTimeout = 0;
      connectTimeout = 10_000;//连接超时默认10秒
      readTimeout = 10_000;//读取超时
      writeTimeout = 10_000;//写超时
      pingInterval = 0;
    }

有时OkHttp的默认配置不能满足我们的需求,例如我们需要将读取超时时间设为30秒并且禁止失败重连。那我们需要通过Builder的build()方法来创建OkHttpClient对象。

final OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(10L, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false)
                .build();

当我们对Buider对象配置完需要修改的参数之后,通过build()方法来创建OkHTTPClient对象。

//OkHttpClient静态内部类
public static final class Builder {
    ...
    public Builder readTimeout(long timeout, TimeUnit unit) {
        readTimeout = checkDuration("timeout", timeout, unit);
        return this;
    }
    ...
    public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) {
          this.retryOnConnectionFailure = retryOnConnectionFailure;
          return this;
    }
    ...
    //  配置完成之后通过此方法创建OkHttpClient对象
    public OkHttpClient build() {
          return new OkHttpClient(this);
    }
}

okhttp建议我们只创建一个OkHttpClient实例并在整个应用中使用它(这是因为多个实例会导致创建多个连接池对象、多个调度器以及多个缓存,浪费内存不说还有可能因为多个chache使用同一个缓存目录导致crash)。而我们的参数配置(如超时、失败重连、拦截器等)是在创建OKHttpClient对象的时候设置的。如果要为某个请求单独配置参数,则需要通过OkHttpClient的newBuilder()方法来创建一个与原始client实例共享连接池、调度器和配置的builder对象。

//原始创建的okhttpClient 对象
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(20L, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false)
                .build();

//某个请求所需的okhttpClient对象
OkHttpClient otherClient = okHttpClient.newBuilder()
                .readTimeout(30L, TimeUnit.SECONDS)
                .build();

源码如下,当调用newBuilder()方法时,会将当前okhttpClient对象(也就是原始的实例对象)传递给Builder的构造器,那我们创建的builder对象则和原始实例对象共用相同的参数。

public Builder newBuilder() {
    return new Builder(this); //将原始的client对象传递给构造器
 }
Builder(OkHttpClient okHttpClient) {
      //新创建的builder对象和原始的client对象使用相同的参数,我们只需将我们所需要改变的参数设置后即可
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      ...
}

以上就是创建OkHttpClient对象的源码分析,总结如下:

  • OkHttpClient对象的创建使用的是建造者设计模式
  • 通过new OkHttpClient()方式创建默认配置的对象
  • 通过new OkHttpClient.Builder()来修改配置然后通过build()方法创建对象
  • 通过OkHttpClient的newBuilder()方法来创建与原始实例对象共享连接池、调度器和配置的Builder对象,通过修改builder对象的部分参数来创建满足单个请求所需要的特殊OkHttpClient对象.

创建请求信息对象(request对象)

Request对象中包含一个URL地址、一个method(请求方法名称)、一个Headers(请求头对象)、一个RequestBody(请求体对象)。Request对象的创建同样是使用的建造者模式,对于请求对象来说url地址是必要的参数,所以request并没有提供默认构造器供我们调用。Request源码稍微简单点,源码如下:

public static class Builder {
    public Builder() {
        this.method = "GET";
        this.headers = new Headers.Builder();
    }
    public Request build() {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this);
    }
}

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

从上面代码可知,默认会指定请求方法为get方法。通过Builder的build方法创建了我们的request对象。

Header内部以字符串数组的形式保存键值对数据。但操作类似于MultiMap。当我们添加请求头时,可以通过header()或者addHeader()方法来添加。从源码可以很直观的看出两者的区别。

public final class Headers {
  private final String[] namesAndValues;//使用数组保存请求头信息
}
//header方法会保证键值对一一对应
public Builder header(String name, String value) {
      headers.set(name, value);
      return this;
}
public Builder set(String name, String value) {
      checkName(name);
      checkValue(value, name);
      removeAll(name);//移除所有name对应的value值
      addLenient(name, value);//将新value值添加进去
      return this;
}
//遍历数组删除name,以及该name对应的值
public Builder removeAll(String name) {
      for (int i = 0; i < namesAndValues.size(); i += 2) {
        if (name.equalsIgnoreCase(namesAndValues.get(i))) {
          namesAndValues.remove(i); // name
          namesAndValues.remove(i); // value
          i -= 2;
        }
      }
      return this;
}
//现将键name添加进去,然后将值value添加进去,保证了偶数位都为键,基数为都为值。
Builder addLenient(String name, String value) {
      namesAndValues.add(name);
      namesAndValues.add(value.trim());
      return this;
}
//addHeader允许一个键对应多个值
public Builder addHeader(String name, String value) {
      headers.add(name, value);
      return this;
}
public Builder add(String name, String value) {
      checkName(name);
      checkValue(value, name);
      return addLenient(name, value);//将值添加到键对应的集合中
}

当我们使用post方式提交数据的时候,数据都是保存在RequestBody(方法体)中的。RequestBody是一个抽象类,可以通过RequestBody 提供的静态方法create()创建。

public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
      final int offset, final int byteCount) {
    if (content == null) throw new NullPointerException("content == null");
    Util.checkOffsetAndCount(content.length, offset, byteCount);
    //直接返回一个匿名内部类对象
    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;//数据类型 指定非空
      }

      @Override public long contentLength() {
        return byteCount;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.write(content, offset, byteCount);//写入数据
      }
    };
  }

从源码可知,创建一个RequestBody对象,需要指定他的数据类型(MediaType)。数据以字节数组(这样就可以传输文件、照片等数据)的形式通过BufferedSink写入。

创建Request对象总结如下:

  • Request对象的创建使用的是建造者模式
  • Request类内部包含url地址、方法名称method、请求头信息headers以及请求体requestBody
  • 请求头信息headers的数据以数组形式存放,支持一对一(通过header()方法添加),一对多(通过addHeader()方法添加)的键值对。
  • 请求体是一个抽象类,可以通过RequestBody的静态方法create()来创建requestBody对象。

执行请求

okHttpClient.newCall(request).execute();

首先通过OkHttpClient的newCall()方法创建一个call对象(call是一个接口,它的实现是RealCall)。

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

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.transmitter = new Transmitter(client, call);
    return call;
  }

RealCall类代码如下,realCall对象持有okhttpClient、request对象的引用。

final class RealCall implements Call {
  final OkHttpClient client;
  private Transmitter transmitter;
  final Request originalRequest;
  final boolean forWebSocket;
  private boolean executed;//是否已经执行

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

对于同步请求来说,通过realCall对象的excute()方法执行该请求。excute()方法中显示调用了dispatcher的excuted()方法,接着调动核心方法getResponseWithInterceptorChain遍历拦截器获取响应,最后调用dispatcher的finished方法结束请求。

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();
    } finally {
      client.dispatcher().finished(this);
    }
  }

okhttpClient的dispatcher()方法直接返回一个Dispatcher(请求调度器对象)。Dispatcher内部维护着三个请求队列和一个线程池对象,源码如下。

public final class Dispatcher {
  private int maxRequests = 64;//最大同时请求数
  private int maxRequestsPerHost = 5;//每个主机最大请求数
  private @Nullable Runnable idleCallback;
  private @Nullable ExecutorService executorService;//线程池对象
  private final Deque readyAsyncCalls = new ArrayDeque<>();//等待执行的异步请求队列
  private final Deque runningAsyncCalls = new ArrayDeque<>();//正在执行的异步请求队列
  private final Deque runningSyncCalls = new ArrayDeque<>();//正在执行的同步请求队列
}

接下来看看Dispatcher的excute()方法,直接将同步请求的call对象添加到同步请求队列中。

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

下面看看核心方法getResponseWithInterceptorChain()的源码。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    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, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
}

方法内部拆分如下:

  • 第一步,将一系列拦截器添加到拦截器集合当中。添加顺序为:自定义的应用拦截器,okhttp定义的默认拦截器,自定义的网络拦截器,CallServerInterceptor(网络请求拦截器)
  • 第二步,通过拦截器集合构造一个拦截器链对象RealInterceptorChain
  • 第三步,通过拦截器器链RealInterceptorChain的proceed()方法递归调用拦截器获得相应response

对于第一步骤,okhttp添加的默认拦截器如下

//用于失败重连,请求跟踪
interceptors.add(new RetryAndFollowUpInterceptor(client));

//用于添加一些默认请求头信息
interceptors.add(new BridgeInterceptor(client.cookieJar()));

//用于配置缓存信息
interceptors.add(new CacheInterceptor(client.internalCache()));

//用于获取网络连接
interceptors.add(new ConnectInterceptor(client));

//真正的执行网络请求获取响应response
interceptors.add(new CallServerInterceptor(forWebSocket));

对于第二步骤,创建拦截器连对象代码如下,这里注意创建时index传的是0

Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

public RealInterceptorChain(List interceptors, Transmitter transmitter,
      @Nullable Exchange exchange, int index, Request request, Call call,
      int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.transmitter = transmitter;
    this.exchange = exchange;
    this.index = index;
    this.request = request;
    this.call = call;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }

对于第三步骤,看看proceed()的源码

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    ...
    //构造新的拦截器链对象
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);

    //获得拦截器集合中的第index位置的对象
    Interceptor interceptor = interceptors.get(index);

    //调用interceptor的intercept方法
    Response response = interceptor.intercept(next);

    ...

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

从上述代码可知会首先将interceptor集合中的第一个拦截器取出来并调用他的intercept()方法。我们以RetryAndFollowUpInterceptor为例来看看他的intercept()方法。

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Transmitter transmitter = realChain.transmitter();

    ...
      try {
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, transmitter, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // The network call threw an exception. Release any resources.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }
  }

在intercept()方法内部又会去调用realChain.proceed()方法,通过index+1,获取到intercept集合中的下一个拦截器对象。继而递归调用所有拦截器的intercept()方法,最后调用到CallServerInterceptor的intercept方法,在CallServerInterceptor中会真正发起网络请求获取响应返回。

到这里整个getResponseWithInterceptorChain()方法执行完毕并返回响应response信息。接着回到realCall的excute()方法,在finally语句快中会调用dispatcher的finish方法。

//同步请求finish()方法,将同步请求队列和请求call交给私有方法finished()处理
void finished(RealCall call) {
    finished(runningSyncCalls, call);//调用内部私有方法finished()
  }

private  void finished(Deque calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      //移除已完成的call对象
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    //从异步等待队列中取出call放入异步执行队列中去执行(针对于异步请求)
    boolean isRunning = promoteAndExecute();

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

以上分析了okhttp同步请求excute()方法的源码,接下来看看异步请求enqueue()方法。异步请求会调用realCall的enqueue()方法。

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

内部调用了dispatcher的enqueue方法,传递对象为AsyncCall,AsyncCall是一个Runnable对象。

void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }

dispatcher的enqueue方法中,直接将请求对象AsyncCall添加到待执行的队列中,然后调用promoteAndExecute()方法。

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

    List executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

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

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        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;
  }

promoteAndExecute()方法逻辑是这样的:首先遍历待执行的异步请求队列readyAsyncCalls,如果当前正在执行的异步请求数量(runningAsyncCalls.size())没有达到最大值,并且每个主机的请求最大值也没打到,则将该异步请求添加到请求集合executableCalls 中。接着遍历executableCalls中的AsyncCall 对象调用它的executeOn()方法执行网络请求。

void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

这里要说明一下AsyncCall是一个Runnable对象,他继承自NamedRunnable,NamedRunnable实现了Runnable接口。线程池执行Runnable对象时会去调用它的run()方法,而在NamedRunnable的run方法中调用了excute()方法,所以最终executorService.execute(this);会触发AsyncCall对象的excute()方法被调用。

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

最后看看AsyncCall的excute()方法

@Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

可知同样调用了getResponseWithInterceptorChain()方法去递归调用拦截器并最终返回response对象,最后调用callback的onresponse方法回调结束本次异步请求。

你可能感兴趣的:(okhttp源码分析第一篇:执行流程)