OkHttp Request 请求执行流程

作者: Joe_H | 来源:发表于2017-03-29 10:53 被阅读0次

    OkHttp不同版本的代码差异挺大的,本篇文章中分析的源码是基于OkHttp 3.6.0版本进行的。


    一、 Request任务的调度

    为了分析OkHttp种一次请求的流程,我们先从最简单的请求示例开始,OkHttp中请求分成同步和异步两种:

    同步请求

    Request request = new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();
    Response response = client.newCall(request).execute();
    

    异步请求

    Request request = new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
        }
    });
    

    这两种请求方式都是通过创建一个Call来发起的,区别在于同步请求直接调用execute()在当前线程执行,而异步请求则是调用enqueue()将任务加入到队列之中,由调度器Dispatcher决定什么时候发起请求。


    sync call

    同步请求在实际开始执行请求之前调用了dispatcher.execute()将自己加入正在执行的请求列表,并在请求结束后调用dispatcher.finish()从执行列表中移除,这主要是为了统一管理执行中的任务。


    async call
    Dispatcher在异步请求中发挥了重要的作用,其中创建了一个线程池用于异步执行请求任务,还可以通过setMaxRequests()设置同时允许执行的最大请求数,以及setMaxRequestsPerHost()设置相同host下最多运行的请求数。
    synchronized void enqueue(AsyncCall call) {
        // 正在执行的总任务数及相同host下正在执行的任务数小于阈值时,直接执行任务
        if(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else { // 加入等待队列
            readyAsyncCalls.add(call);
        }
    }
    

    当正在执行的任务总数及相同host下的任务数小于最大值时,直接执行当前请求,而任务数超过限定时,将其加入等待队列。

    AsyncCall继承于Runnable,相当于是对RealCall的一次包装。

    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 {
            responseCallback.onFailure(RealCall.this, e);
          }
        } finally {
        // 通知 dispatcher 已完成当前请求,开始执行任务调度
        client.dispatcher().finished(this);
        }
      }
    }
    

    getResponseWithInterceptorChain()中完成实际的网络请求,在执行完毕后调用finished方法通知dispatcher执行任务调度,最终的调度任务在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; 
      }
    }
    

    每次异步请求执行结束后都会拉起正在等待队列中的请求任务。

    二、Request执行流程

    上面我们看到实际的请求是在getResponseWithInterceptorChain()中完成的,那么我们来看看其中发生了什么。

    Response getResponseWithInterceptorChain() throws IOException {
      // Build a full stack of interceptors.
      List<Interceptor> interceptors = new ArrayList<>();
      // 自定义拦截器
      interceptors.addAll(client.interceptors()); 
      // 重定向拦截器
      interceptors.add(retryAndFollowUpInterceptor); 
      // 桥接拦截器(处理header 、cookie 等)
      interceptors.add(new BridgeInterceptor(client.cookieJar())); 
      // 缓存拦截器(处理 cache)
      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);
      return chain.proceed(originalRequest);
    }
    

    这里创建了一个 Interceptor 的列表,按照用户自定义的 Interceptor、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、自定义的 network Interceptor、CallServerInterceptor的顺序将这些拦截器添加到列表中,后面可以看到这个顺序就是请求实际执行的顺序。

    在请求的过程中,OkHttp 使用了责任链模式来一步步完成请求,这个责任链就是由RealInterceptorChain来实现的。

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection) throws IOException {
      // 省略部分无关代码
    
      // Call the next interceptor in the chain.
      RealInterceptorChain next = new RealInterceptorChain(
          interceptors, streamAllocation, httpCodec, connection, index + 1, request);
      Interceptor interceptor = interceptors.get(index);
      Response response = interceptor.intercept(next);
    
      // ...
    
      return response;
    }
    

    在 proceed 中根据 index 依次获取之前传入的 interceptor 处理请求,同时创建了链式节点传递给下一个拦截器,以便下一个拦截器处理完自己的任务后调用。以ConnectInterceptor为例:

    public final class ConnectInterceptor implements Interceptor {
      public final OkHttpClient client;
    
      public ConnectInterceptor(OkHttpClient client) {
        this.client = client;
      }
    
      @Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
    
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        // 创建解码器,并建立连接
        HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
        // 将请求交给下一个拦截器处理
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
      }
    }
    

    OkHttp 中的请求流程可以用下面这张图来描述。


    Request 请求流程

    总之,请求是以链式的形式一步步进行,所有的拦截器完成自己的任务后就将请求传递给下一个拦截器进行处理,直到最后请求执行完毕。

    相关文章

      网友评论

        本文标题:OkHttp Request 请求执行流程

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