彻底掌握网络通信(十八)走进OkHttp3的世界(三)详解Http请求的连接,发送和响应

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yi_master/article/details/82658291

彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
彻底掌握网络通信(十一)HttpURLConnection进行网络请求的知识准备
彻底掌握网络通信(十二)HttpURLConnection进行网络请求概览
彻底掌握网络通信(十三)HttpURLConnection进行网络请求深度分析
彻底掌握网络通信(十四)HttpURLConnection进行网络请求深度分析二:缓存
彻底掌握网络通信(十五)HttpURLConnection进行网络请求深度分析三:发送与接收详解
彻底掌握网络通信(十六)走进OkHttp3的世界(一)引言
彻底掌握网络通信(十七)走进OkHttp3的世界(二)请求/响应流程分析

  我们知道当我们实例化一个call之后(具体实现为RealCall),其会调用RealCall的enqueue方法,

//RealCall.java
  @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,这个AsyncCall有几个特点:

  1. AsyncCall为RealCall的内部类,顾其能拿到外部类的重要属性,如可以拿到我们发起的请求Request
  2. AsyncCall实现了Runnable接口,每一个http请求的执行其实就是AsyncCall的execute方法被执行

实例化AsyncCall之后,会获得请求分发者Dispatcher对象,并调用Dispatcher的enqueue方法

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

第3行~第5行:如果runningAsyncCalls队列大小小于64并且如果同一个host的请求数小于5的情况下,会将当前的call添加到runningAsyncCalls队列中,并在线程池中来执行这个call
第6行~第7行:相反的则将当前call添加到readyAsyncCalls中


**Dispatcher**中,有几个重要属性我们需要重点看下
  1. readyAsyncCalls
  2. runningAsyncCalls
  3. runningSyncCalls
  4. cancelAll()方法
  5. promoteCalls()方法
  6. finished()方法
  7. queuedCalls()方法
  8. runningCalls()方法

在enqueue方法中我们知道

  1. runningAsyncCalls添加的是正在执行的call
  2. readyAsyncCalls是当队列内容过多,正在排队的call

疑问1:runningSyncCalls是什么?

我们先看下如下代码

        OkHttpClient mOkHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
                .addHeader("Accept", "application/json; q=0.5")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.execute();

这段代码中,我们直接调用了call的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);
    }
  }

第9行:调用Dispatcher的executed方法,executed方法作用就是将当前call添加到runningSyncCalls列表中;
第10行:通过getResponseWithInterceptorChain方法获取响应;这里也是很关键的方法,会在后面详述

runningSyncCalls 代表的是一个同步执行call的列表,每一个同步执行的call都会被添加到这个列表中


1: Dispatcher.java cancelAll() 详解
  public synchronized void cancelAll() {
    for (AsyncCall call : readyAsyncCalls) {
      call.get().cancel();
    }

    for (AsyncCall call : runningAsyncCalls) {
      call.get().cancel();
    }

    for (RealCall call : runningSyncCalls) {
      call.cancel();
    }
  }

该方法会调用call的cancel方法

  @Override public void cancel() {
    retryAndFollowUpInterceptor.cancel();
  }

我们知道call的实现者为RealCall,在实例化RealCall的时候,我们便会创建 RetryAndFollowUpInterceptor 这样一个实例,这个类代表一个请求的重试和重定向能力,如果调用retryAndFollowUpInterceptor的cancel方法则表示取消当前请求,并关闭当前socket和释放资源

cancelAll方法作用就是取消全部的正在运行的,等待运行的请求


2: Dispatcher.java promoteCalls() 详解
  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    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; // Reached max capacity.
    }
  }

关键代码就在第5行~第12行
第5行: 遍历等待运行的call
第8行~第11行:如果一个host上同时请求的call小于5个,则将此call从等待执行的队列中移除,并添加到正在运行的请求队列中,同时运行该call

promoteCalls方法作用就是将等待执行的call从等待队列中移除,并将该call添加到正在执行的队列当中,并开始执行这个call

3: Dispatcher.java finished() 详解

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

  /** Used by {@code Call#execute} to signal completion. */
  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();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

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

第14行,如果将call从runningAsyncCalls移除失败,抛出一个error
第15行,参考上面的分析
第16行,获得同步或者异步请求的总数量
第17行,对一个runable进行赋值
第21行,当所有请求都执行完成之后,这个idleCallback的run方法将被执行

疑问2:idleCallback怎么使用尼?
这个idleCallback是一个Runnable类型,他使用的场景如下
当客户端发起N多个网络请求,当这些请求全部被执行完之后,这个idleCallback的run方法将会被回调

       OkHttpClient mOkHttpClient = new OkHttpClient();
       mOkHttpClient.dispatcher().setIdleCallback(new Runnable() {
            @Override
            public void run() 
	           //当run方法被执行的时候,则说明所有的请求都执行完毕了     
            }
        });

4: Dispatcher.java queuedCalls() 详解

  public synchronized List<Call> queuedCalls() {
    List<Call> result = new ArrayList<>();
    for (AsyncCall asyncCall : readyAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

这段代码比较简单,就是返回全部的异步请求列表

5: Dispatcher.java runningCalls() 详解

  public synchronized List<Call> runningCalls() {
    List<Call> result = new ArrayList<>();
    result.addAll(runningSyncCalls);
    for (AsyncCall asyncCall : runningAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

返回全部的同步请求列表

疑问:怎么取消一个正在执行的请求?

我们知道我们可以通过queuedCalls或者runningCalls方法获得全部的异步或者同步请求列表,而call提供了cancel的方法来取消一个请求

那么具体的代码就是这样的

        OkHttpClient mOkHttpClient = new OkHttpClient();
        mOkHttpClient.dispatcher().queuedCalls().get(0).cancel();

当然了这个是比较粗糙的方法,精细点的你可以通过如下代码来取消操作

        OkHttpClient mOkHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .tag("request1")
                .url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
                .addHeader("Accept", "application/json; q=0.5")
                .build();

        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            if ("request1".equals(call.request().tag())) {
                call.cancel();
            }
        }

备注:这里的取消请求,并非是把发出去的请求拉回来,而是让这个请求回调onFail方法

简单总结下

Dispatcher类作为一个分发者,

  1. 在其内部保持三个重要的队列,分别用来存储异步请求,同步请求和超出异步队列大小的待执行队列
  2. 当实例化call之后,便会添加到对应队列中
  3. Dispatcher里面维护的线程池可以使异步call执行起来
  4. 同时Dispatcher提供了查询全部请求的能力,和取消全部请求的能力等等
  5. 最后一个比较偏门的地方就是,他提供了一个Runable类型的idleCallback,如果所有的请求均完成,该runable的run方法即可被执行

当创建 RealCall 之后,我们就可以将这个请求发送出去了,这里以一个异步的call发送过程为例子进行分析

RealCall内部有几个重要属性

  1. OkHttpClient client
  2. RetryAndFollowUpInterceptor retryAndFollowUpInterceptor //重试,重定向的拦截器
  3. Request originalRequest //发送的请求

在RealCall构造函数中,会完成上述属性的赋值,其中forWebSocket为false

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

一个异步请求其本质上是RealCall的内部类AsyncCall的执行,当一个异步请求被Dispatcher中的线程池执行的时候,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);
      }
    }
  }

第4行: 获得服务端的响应
第5行:当客户端调用call的cancel方法之后,此处即为true,回调onFailure方法
第10行:请求完成之后,回调onResponse方法
第21行:当请求完成之后,调用Dispatcher的finish方法,将此请求从runningAsyncCalls队列中移除,并从readyAsyncCalls队列中获取一个等待请求,并发起该请求


我们重点看下第4行的方法getResponseWithInterceptorChain()

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

    return chain.proceed(originalRequest);
  }

在这段代码中,集合interceptors添加了很多了内容,他们有

  1. client.interceptors()
  2. retryAndFollowUpInterceptor
  3. BridgeInterceptor
  4. CacheInterceptor
  5. ConnectInterceptor
  6. client.networkInterceptors()
  7. CallServerInterceptor

统统7个内容,而这几个内容也是贯穿OkHttp的整个环节中重要的类;我们应该详细了解一下

RealCall的构造函数中,已经创建RetryAndFollowUpInterceptor类的实例,顾我们先看下这几个拦截器的继承关系
这里写图片描述

可见这些拦截器均实现了 Interceptor 接口

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }

所有的拦截器都实现了Interceptor 接口,也就是说每一个拦截器都会触发intercept方法;

第2行:定义了intercept方法,传入Chain作为参数

第4行,定义了Chain接口,这个接口有几个关键方法

  1. Response proceed()
  2. Request request()
  3. Connection connection()

这样的定义的好处是什么?我们返回到getResponseWithInterceptorChain方法的第14行,他创建了一个RealInterceptorChain的实例,很明显这个类实现了Interceptor.Chain接口;
第15行,调用Interceptor.Chain接口的proceed方法返回请求响应


这里不经就产生了一个疑问,这个 RealInterceptorChain 是做什么用的,他和拦截器有什么关系


为了了解这个问题的答案,我们看下RealInterceptorChain类持有的几个重要成员
  1. private final List interceptors;
  2. private final StreamAllocation streamAllocation;
  3. private final HttpCodec httpCodec;
  4. private final RealConnection connection;
  5. private final int index;
  6. private final Request request;
  7. private final Call call;

第一个成员就是他持有全部的拦截器,通过这个类可以获得全部的拦截器
第二个成员他是打通客户端到服务端socket连接并保持流的关键类
第四个成员这个就跟我们之前的HttpUrlConncetion里面一样,他是一个客户端到服务端的连接
第五个成员便是用来从interceptors列表中获得拦截器的索引

接着分析下这个类的proceed方法

  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

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

第7行,如果index超过interceptors集合大小,显然或得不到拦截器,顾抛出异常
第24行,创建RealInterceptorChain实例,此时传入的第五个参数值为1,
第27行,通过index获得第一个拦截器
第28行,传入刚刚创建的RealInterceptorChain实例,调用拦截器的intercept方法

我们以RetryAndFollowUpInterceptor的intercept方法为例,看下这个RealInterceptorChain的工作流程是什么

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();

    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;
    /* 省略部分代码*/
    Response response;
      
    try {
       response = realChain.proceed(request, streamAllocation, null, null);      
	} catch (RouteException e) {	  
	}
        
}

大家注意第14行,纵观所有的拦截器,在拦截器的intercept方法中,均会调用chain的proceed方法;


这里对RealCall和RealInterceptorChain做一个总结:

  1. 当call被执行的时候,getResponseWithInterceptorChain方法将会被执行
  2. getResponseWithInterceptorChain会添加7中类型的拦截器,每个拦截器实现了Interceptor接口
  3. getResponseWithInterceptorChain第一次创建实现了Chain接口的类RealInterceptorChain,RealInterceptorChain通过索引可以获得全部的拦截器
  4. 通过调用RealInterceptorChain类的proceed方法将所有的拦截器功能运行起来
    4.1 再次构造一个RealInterceptorChain实例,其中获得拦截器的索引+1,这样既可获得下一个拦截器
    4.2 调用拦截器的intercept方法
    4.3 拦截器的intercept方法又会回调RealInterceptorChain的proceed方法,通过这样的循环,所有的拦截器功能就执行起来了

那我们不经又问了这些拦截器都会被执行吗?一个响应必须是让全部的拦截器都执行完毕吗?

不是的,

我们知道RealInterceptorChain维护了7种拦截器,

如果一个请求在 没有缓存的 情况下,一个call被执行后,其流程是
这里写图片描述

  1. 创建RealInterceptorChain实例,并调用其proceed方法
  2. 在proceed方法里面,再次创建一个RealInterceptorChain,第一步和第二步的实例区别在于第五个参数的值不同,第二个实例中第五个参数值+1了,这样就能获得下一个拦截器
  3. 获得第一个拦截器,(我们这里以RetryAndFollowUpInterceptor为第一个拦截器为例子),将第二个RealInterceptorChain作为参数传入拦截器的intercept方法中,并执行该方法
  4. 第一个拦截器在执行自己的intercept方法之后,会使用传入的RealInterceptorChain参数,调用其proceed方法
    这样重复第2步,这样一个链式结构的请求就结束了

  那一个请求的response是如何返回的 经过上面的分析,每一个拦截器的intercept都会返回一个response,同时拦截器是自上而下执行的,顾这个response便会采用冒泡的方式逐步返回RealCall;顾我们倒着看拦截器的执行就能知道response是如何返回的了
  1. CallServerInterceptor : 负责和服务端进行交互,拿到请求的响应
  2. ConnectInterceptor: 负责和服务端进行连接,创建socket连接
  3. CacheInterceptor: 对上面的拿到的response进行缓存处理
  4. BridgeInterceptor: 对上层发起的请求做一次补充处理,补充缺少的头信息,然后传给下一个拦截器进行请求;同时当response返回的时候,对response做一次处理,然后返回给retryAndFollowUpInterceptor
  5. retryAndFollowUpInterceptor:开启一个while循环,针对response来判断是否需要重新定向或者重试
  6. 最后返回给RealCall

完整的流程图如下
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yi_master/article/details/82658291