美文网首页
Okhttp3 同步请求,源码浅析(一)

Okhttp3 同步请求,源码浅析(一)

作者: 徘徊0_ | 来源:发表于2020-05-08 10:06 被阅读0次

使用Okhttp3 进行一次同步请求,大概需要进行下面几部操作:
注:同步请求是阻塞式,直到HTTP响应返回。

        try {
            //1,构建 client
            OkHttpClient client = new OkHttpClient.Builder().readTimeout(10,TimeUnit.SECONDS).build();//可以通过添加各种属性
            //2,构建request请求
            Request request = new Request.Builder().url("www.baidu.com").build();//可以继续添加各种数据
            //3,获得call对象
            Call call = client.newCall(request);
            //4,执行同步请求
            Response response = call.execute();
            //...
        } catch (IOException e) {
            e.printStackTrace();
        }

按照上面同步请求的步骤跟踪源码进行一下简单的分析:

1,OkhttpClient

这里使用到了Builder设计模式,来构建一个OkhttpClient对象,可以设置缓存、超时、cookie管理等属性(如下)。

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.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;//cookie
    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;

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

2,Request

通过Builder构建一个请求,其中包含

  • url 请求的地址
  • methodget 、post
  • headers请求头
  • RequestBody请求体
3,Call
public interface Call extends Cloneable {.....}

源码可以看出,Call是一个接口,需要找实现它的类RealCall,具体的实现,都在RealCall

final class RealCall implements Call {.....}

从上面同步请求的步骤3可以看出Call的创建过程:Call call = client.newCall(request); client 调用OkHttpClient.newCall方法,其实就是调用了RealCall.newRealCall得到一个 Call 如下,

//OkHttpClient.java

/**
   * 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.java

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;
  }
4,call.execute()执行同步方法

在执行同步方法的时候,call.execute() 实际上也是执行的RealCall .execut()

@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); //在这里,调用了分发器:Dispatcher.executed() 方法
      Response result = getResponseWithInterceptorChain();//这个方法,生成拦截器链,后续分析
      if (result == null) throw new IOException("Canceled");
      return result;//将结果response返回
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);//最后的时候,会调用分发器:Dispatcher.finished() 方法
    }
}
  • 分析一下上面的client.dispatcher().executed(this);这里拿到的是OkhttpClient.dispatcher,并且执行了Dispatcher.executed()方法
//Dispatcher.java

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

//
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  • client.dispatcher().finished(this); 看一下这个方法,会调用到下面的
  //同步的finished,会调到这里
  void finished(RealCall call) {
    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(); //此时,promoteCalls = false 不会进入promoteCalls();
      runningCallsCount = runningCallsCount();//重新计算一下,正在运行的数量
      idleCallback = this.idleCallback;
    }

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

相关文章

网友评论

      本文标题:Okhttp3 同步请求,源码浅析(一)

      本文链接:https://www.haomeiwen.com/subject/qbkarqtx.html