美文网首页
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