3.OkHttp请求流程分析

作者: Mirs | 来源:发表于2017-06-29 12:09 被阅读0次

    Call和RealCall

    经过上面的初始化之后 okhttpClient 调用public Call newCall(Request request) 方法去构建一个Call,

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

    可以看到 真实的Call是RealCall ,get知识点 一般来说,一个组织或者个人的代码风格是差不多的 这里面Call的实现类是RealCall,其他的应该也是Real开头的。

    这里我们发现他把OkHttpClient和Reques传了过去,他的构造方法是

      RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
    
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        //重试和跟进拦截器
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    
        // TODO(jwilson): this is unsafe publication and not threadsafe.  
        // 这是不安全的发布,不是线程安全的。
        this.eventListener = eventListenerFactory.create(this);
      }
    
    

    这里面比较面生的是的是RetryAndFollowUpInterceptor 按照字面意思重试和跟进拦截器 进去大概看一下

    /**
     * This interceptor recovers from failures and follows redirects as necessary. It may throw an
     * {@link IOException} if the call was canceled.
     * 这个拦截器从故障中恢复,并根据需要遵循重定向。如果呼叫被取消,它可能会抛出IOException。
     */
    public final class RetryAndFollowUpInterceptor implements Interceptor {
      /**
       * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
       * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
       * 我们应该尝试多少次重定向和认证挑战? Chrome遵循21次重定向; Firefox,curl和wget遵循20; Safari遵循16; HTTP / 1.0建议5。
       */
      private static final int MAX_FOLLOW_UPS = 20;
    
      private final OkHttpClient client;
      private final boolean forWebSocket;
      private StreamAllocation streamAllocation;
      private Object callStackTrace;
      private volatile boolean canceled;
    
      public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
        this.client = client;
        this.forWebSocket = forWebSocket;
      }
      
      (....此处省略以后的代码)
    

    果然和字面意思一样,这面看参数的话最大支持20次的重定向。后面的暂时不需要看 等用到的时候再看也不迟,避免陷入之间树木不见森林的坑

    后面又用工厂方法创建了一个EventListener 的类,看字面意思就是时间的监听类看里面的方法

    Factory
    fetchStart
    dnsStart
    dnsEnd
    connectStart
    secureConnectStart
    secureConnectEnd
    connectEnd
    requestHeadersStart
    requestHeadersEnd
    requestBodyStart
    requestBodyEnd
    responseHeadersStart
    responseHeadersEnd
    responseBodyStart
    responseBodyEnd
    fetchEnd
    

    通过这个事件我们大致能看出来OkHttp请求的流程,然后回到RealCall ,之后调用的是一个execute或者是enqueue 我们在Android项目里由于主线程是不允许有网络请求的,所以我们先来搞enqueue,话不多说,进去看

    @Override 
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        //捕获呼叫堆栈跟踪
        captureCallStackTrace();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    

    Already Executed 这个异常大家在平时用的时候应该偶尔会碰到,原因看到了吧。当你的这个请求应在运行的时候你在去调用的时候就异常了

    然后第二个是捕获呼叫堆栈跟踪器,这个就忽略掉,看真正的重头戏

    前方高能预警,提起精神看

    Dispatcher

    相关的更详细的分析在第五篇 OkHttp的请求调度分析

    调用的OkHttpClient 的dispatcher的enqueue方法,dispatcher的初始化是在OkHttpClient的Builder里面 看上面的代码 是直接new 了一个 Dispatcher ,我们去dispatcher里看

    public final class Dispatcher {
      private int maxRequests = 64;
      private int maxRequestsPerHost = 5;
      private Runnable idleCallback;
    
      /** Executes calls. Created lazily. */
      private ExecutorService executorService;
    
      /** Ready async calls in the order they'll be run. 按照他们将要运行的顺序进行准备就绪的异步调用 */
      private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
      /** Running asynchronous calls. Includes canceled calls that haven't finished yet. 运行异步调用包括尚未完成的取消请求*/
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
      /** Running synchronous calls. Includes canceled calls that haven't finished yet.  运行同步调用包括尚未完成的取消呼叫*/
      private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    
      public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
      }
    
      public Dispatcher() {
      }
      
      (....此处省略N行代码)
      
       synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    }
    

    我们看到他的构造函数就是一个空的 然后全局变量的话有个线程池,和三个双端队列 有可能有同学不知道Deque是什么,deque 即双端队列。是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。

    我们看到enqueue里面就是操作运行异步调用包括尚未完成的取消请求的runningAsyncCalls,他的判断条件是:

    1.当前队列里面的请求数量小于最大请求数也就是64

    2.当前队列里面的链接的总host数量小于最大请求Host数

    如果条件成立就添加到这个队列里面,否则的话就添加到readyAsyncCalls里,也就是按照他们将要运行的顺序进行准备就绪的异步调用的队列

    加入到运行队列里后,执行executorService().execute(call);方法这个方法就是个new出了一个线程池,然后执行

      public synchronized ExecutorService executorService() {
        if (executorService == null) {
          executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
              new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
      }
    

    可能关于线程池的一些东西大家不是特别清楚 这里稍微解释一下,首先是他的构造函数

    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
    BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 
    corePoolSize: 线程池维护线程的最少数量 
    maximumPoolSize:线程池维护线程的最大数量 
    keepAliveTime: 线程池维护线程所允许的空闲时间 
    unit: 线程池维护线程所允许的空闲时间的单位 
    workQueue: 线程池所使用的缓冲队列 
    handler: 线程池对拒绝任务的处理策略
    

    上面的SynchronousQueue可能一般同学看的不是特别熟悉这里解释一下:

    SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。

    SynchronousQueue的一个使用场景的典型就是在线程池里。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。执行是调用execute方法。

    峰回路转 ,回到ReallCall的enqueue里面

    这里执行的正式AsyncCall,new AsyncCall(responseCallback) AsyncCalls是RealCall的一个内部类,继承NamedRunnable,NamedRunnable是一个实现了Runnable接口的抽象类,

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

    这里他做了两件事

    1. 给当前线程设设置了个名字
    2. 新增了一个抽象方法execute

    把握住这两个 再来看AsyncCall

    AsyncCall

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
    
        AsyncCall(Callback responseCallback) {
          //命名规则 OkHttp+协议名+域名
          super("OkHttp %s", redactedUrl());
          this.responseCallback = responseCallback;
        }
    
        String host() {
          return originalRequest.url().host();
        }
    
        Request request() {
          return originalRequest;
        }
    
        RealCall get() {
          return RealCall.this;
        }
    
        @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 {
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            //调用dispatcer的finshed方法,(重要,在下文的请求调度分析会将)
            client.dispatcher().finished(this);
          }
        }
      }
    

    上面比较重要的是这一句

      Response response = getResponseWithInterceptorChain();
    

    如果重试和跟进拦截器没有被取消的话,返回请求成功调用 responseCallback.onResponse,如果中间有什么异常的话调用responseCallback.onFailure(RealCall.this, e);

    getResponseWithInterceptorChain() 这个方法非常重要,是整个OkHttp请求的核心,他是组装了一系列的拦截链,进行链式调用,最后返回组装的请求结果

     /*一共五个拦截器  包括
      *   RetryAndFollowUpInterceptor 重试和跟进拦截器
      *   BridgeInterceptor           桥拦截器
      *   CacheInterceptor            缓存拦截器
      *   ConnectInterceptor          链接拦截器
      *   CallServerInterceptor       呼叫服务拦截器
      *
      *   RealInterceptorChain        实际拦截链
      * */
      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);
        return chain.proceed(originalRequest);
      }
    

    接下来 我们来走进这些请求链的世界,去分析整个OkHttp请求的具体

    相关文章

      网友评论

        本文标题: 3.OkHttp请求流程分析

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