美文网首页
OkHttp 源码分析

OkHttp 源码分析

作者: CodeDuan | 来源:发表于2022-02-24 14:30 被阅读0次

    一、概述

    在日常开发中最常用的请求框架就是Okhttp了,本文将对okhttp的请求流程 由浅入深进行分析,由于我项目中使用的Okhttp版本为3.12.0,所以我们根据3.12.0的源码进行分析。

    Okhttp特点:
    OkHttp 是一个默认高效的 HTTP 客户端:

    • HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
    • 连接池减少了请求延迟(如果 HTTP/2 不可用)。
    • 透明 GZIP 缩小了下载大小。
    • 响应缓存完全避免了网络重复请求。

    二、源码分析

    在源码分析之前,我们先看一下Okhttp的简单使用。

            //创建一个OkhttpClient
            OkHttpClient client = new OkHttpClient();
            //创建Request
            Request request = new Request.Builder()
                    .url("https://www.baidu.com")
                    .build();
            //同步请求
            Response response = client.newCall(request).execute();
    

    这样就很简单的实现了一个请求,我们一点一点来分析,先来看OkhttpClient

    public OkHttpClient() {
        this(new Builder());
      }
    
    OkHttpClient(Builder builder) {
        //省略部分代码
        this.dispatcher = builder.dispatcher;
        this.proxy = builder.proxy;
        this.protocols = builder.protocols;
        //省略部分代码
      }
    

    可以看到在创建OkHttpClient时,使用的是建造者模式,最终创建出OkhttpClient,Builder的代码太长这里就不贴了,就是对一些配置的初始化,例如超时时间等,另外我们可以通过这个Builder.build()方法来构建出OkhttpClient。

    接下来我们再来看Request是什么:

    public final class Request {
      final HttpUrl url;
      final String method;
      final Headers headers;
      final @Nullable RequestBody body;
      final Map<Class<?>, Object> tags;
    
      private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
    
      Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags);
      }
        //省略部分代码
    }
    

    Request包含的就是请求的url,method,header,RequestBody等信息。同样是通过Builder来构建。

    最后就是执行请求了,到这里之后就是重点了,好好看哦,

            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
    
                }
            });
    

    同样我们按照顺序,先来看client的newCall方法做了什么:

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

    newCall方法返回值为Call,返回了一个RealCall(Call的实现类)。

    既然newCall返回的是RealCall,那么enqueue方法就一定再RealCall里面,就是说我们的同步/异步请求最终都是通过这个RealCall来执行的。所以我们来看RealCall的enqueue()方法:

      @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
            //如果为true 直接抛出异常,说明这个请求已经执行了
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
          //事件监听回调
        eventListener.callStart(this);
          //重点
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    

    上面的最后一行代码又调用了client.dispatcher().enqueue()方法,就是Dispatcher类的enqueue(),我们来点进去看:

      void enqueue(AsyncCall call) {
        synchronized (this) {
          readyAsyncCalls.add(call);
        }
        promoteAndExecute();
      }
    

    enqueue方法又将call加入了readyAsyncCalls,我们先来看看这是啥:

      /** 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<>();
    

    在Dispatcher类中有三个双端队列:
    readyAsyncCalls:准备队列,
    runningAsyncCalls:运行时异步队列
    runningSyncCalls:运行时同步队列

    所以说上面的enqueue方法中,就是将call先放到了准备队列,然后调用了promoteAndExecute()方法,我们来看看这个方法:

    private boolean promoteAndExecute() {
        assert (!Thread.holdsLock(this));
    
        //用于保存要执行请求的队列
        List<AsyncCall> executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) {
          for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall asyncCall = i.next();
    
              //如果运行中队列大于maxRequests 64 终止执行
              //就是正在请求的数量 不能大于64
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
              //如果相同host同时请求大于maxRequestsPerHost 结束本次循环
              //就是对同一个主机的同时请求 不能大于5
            if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
    
            //符合条件时,将请求从准备队列中移除
            i.remove();
            //将请求加到执行队列
            executableCalls.add(asyncCall);
            //将请求加到运行中队列
            runningAsyncCalls.add(asyncCall);
          }
          isRunning = runningCallsCount() > 0;
        }
    
        //遍历执行队列
        for (int i = 0, size = executableCalls.size(); i < size; i++) {
          AsyncCall asyncCall = executableCalls.get(i);
          //执行请求
          asyncCall.executeOn(executorService());
        }
    
        return isRunning;
      }
    

    promoteAndExecute()方法的主要操作就是将符合执行条件的请求执行,最终调用的是asyncCall.executeOn(executorService())方法,下面我们先来看executorService()方法:

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

    这个方法其实就是返回了一个线程池,所以说异步请求是通过线程池来处理的,我们再返回去看asyncCall.executeOn()方法:

        void executeOn(ExecutorService executorService) {
          assert (!Thread.holdsLock(client.dispatcher()));
          boolean success = false;
          try {
            //通过线程池 执行请求 this = RealCall
            executorService.execute(this);
            success = true;
          } catch (RejectedExecutionException e) {
            //异常处理
            InterruptedIOException ioException = new InterruptedIOException("executor rejected");
            ioException.initCause(e);
            eventListener.callFailed(RealCall.this, ioException);
            responseCallback.onFailure(RealCall.this, ioException);
          } finally {
            if (!success) {
                //
              client.dispatcher().finished(this); // This call is no longer running!
            }
          }
        }
    

    在上面的方法中,调用了线程池的execute(this)方法,就相当于调用了RealCall的execute,继续来看这个方法:

    @Override protected void execute() {
          boolean signalledCallback = false;
          timeout.enter();
          try {
              //这里最终通过拦截器 来拿到response
            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) {
            e = timeoutExit(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 {
              //最终调用了finished
            client.dispatcher().finished(this);
          }
        }
    

    RealCall的execut方法就是通过拦截器来拿到response,最终调用了client.dispatcher().finished(this)方法,拦截器我们先放在后面讲,先继续看client.dispatcher().finished(this):

    //最终调用的这个方法
    private <T> void finished(Deque<T> calls, T call) {
        Runnable idleCallback;
        synchronized (this) {
          if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
          idleCallback = this.idleCallback;
        }
    
        //这里是重点 这里又调用了promoteAndExecute()
        boolean isRunning = promoteAndExecute();
    
        if (!isRunning && idleCallback != null) {
          idleCallback.run();
        }
      }
    

    到这里是不是有一点豁然开朗了?这个方法里又调用了promoteAndExecute()方法,还记得刚开始的enqueue里面也是执行了这个方法嘛?所以说当一个请求结束后,又再次处理队列中的其他请求。

    到这里整个请求的流程就结束了。我们继续来看一下上面提到的拦截器;

    Response response = getResponseWithInterceptorChain();
    
    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        //声明一个list 存储拦截器
        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));
        //自定义的的network拦截器
        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);
      }
    

    在okhttp中有五种拦截器:重试拦截器,桥接拦截器,缓存拦截器,连接拦截器,请求服务拦截器,另外还有用户自定义的拦截器。其中拦截器是责任链模式,通过层层调用最终返回我们想要的Response,拦截器暂时不深入介绍了,后面有时间再研究吧。

    异步请求看完之后,接下来就剩同步请求了,其实同步请求更简单,

    @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        timeout.enter();
        eventListener.callStart(this);
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          e = timeoutExit(e);
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    
     synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    

    同步请求则直接将请求通过dispatcher加入了运行中同步队列中,其他处理与异步请求大致相同,这里就不做太多介绍了。

    三、总结

    至此我们已经分析完了Okhttp请求的处理流程,我们最后总结一下:

    1. 创建OkhttpClient
    2. 构建Request请求
    3. newCall得到RealCall
    4. 通过RealCall调用enqueue/execute
    5. 通过dispatcher进行任务分发
    6. 最终通过拦截器得到Response

    最后画一张流程图仅供参考:


    img.jpg

    相关文章

      网友评论

          本文标题:OkHttp 源码分析

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