美文网首页android
okhttp源码解析--同步|异步请求流程

okhttp源码解析--同步|异步请求流程

作者: 二妹是只猫 | 来源:发表于2019-02-12 13:23 被阅读3次
    Okhttpclient流程图.png

    基础分析(深入分析(dispatcher、拦截器等)流程较长且更复杂,后面会单独介绍)

    1. 基本请求流程

    
    //1.创建okhttpclient和Request对象,并设置一些配置
    
    OkHttpClient okHttpClient =new OkHttpClient().newBuilder().connectTimeout(5,TimeUnit.SECONDS).build();
    
    Request request =new Request.Builder().url("https://www.baidu.com").get().build();
    
    //2.将request封装成call对象
    Call call = okHttpClient.newCall(request);
    
    try {
    
      //3.调用Call的execute()发送同步请求
    
        Response response = call.execute();
    
      //4.call执行异步请求
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        
                    }
    
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
    
                    }
                });
    }catch (IOException e) {
    
    e.printStackTrace();
    
    }
    
    
    1.1 创建okhttpclient和Request对象,并进行初始化配置:
      public Builder() {
          dispatcher = new Dispatcher();
          protocols = DEFAULT_PROTOCOLS;
          connectionSpecs = DEFAULT_CONNECTION_SPECS;
          eventListenerFactory = EventListener.factory(EventListener.NONE);
          proxySelector = ProxySelector.getDefault();
          cookieJar = CookieJar.NO_COOKIES;
          socketFactory = SocketFactory.getDefault();
          hostnameVerifier = OkHostnameVerifier.INSTANCE;
          certificatePinner = CertificatePinner.DEFAULT;
          proxyAuthenticator = Authenticator.NONE;
          authenticator = Authenticator.NONE;
          connectionPool = new ConnectionPool();
          dns = Dns.SYSTEM;
          followSslRedirects = true;
          followRedirects = true;
          retryOnConnectionFailure = true;
          connectTimeout = 10_000;
          readTimeout = 10_000;
          writeTimeout = 10_000;
          pingInterval = 0;
        }
    

    使用建造者进行创建,这种模式适合于当需要初始化很多内容的时候。
    注意其中dispatcher分发器,ConnectionPool链接池(可将我们的一个个请求理解为链接,链接池就是来管理这些链接的)主要作用:1.当请求的url是相同的时候可选择复用 2.设置链接策略。 后面会详细介绍

    
    /**
     * Policy on when async requests are executed.
     *
     * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
     * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
     * of calls concurrently.
     */
    public final class Dispatcher {
      private int maxRequests = 64;
      private int maxRequestsPerHost = 5;
      private @Nullable Runnable idleCallback;
    
      /** Executes calls. Created lazily. */
      private @Nullable 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() {
      }
    
      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;
      }
    

    dispatcher内部维护着网络请求的线程池,分别是readyAsyncCalls(等待中的同步请求队列)、runningAsyncCalls(请求中的异步请求队列)、runningSyncCalls(请求中的 同步请求队列)。它的代码量不多却是okhttp的核心之一。

    1.2 将request封装成call对象
    /**
       * Prepares the {@code request} to be executed at some point in the future.
       */
      @Override public Call newCall(Request request) {
        return new RealCall(this, request, false /* for web socket */);
      }
    

    newcall方法实际创建了call接口的实现类RealCall:

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

    这里的OkHttpClient,requst引用过来了

    1.3 调用Call的execute()发送同步请求
      @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    

    getResponseWithInterceptorChain()这里实际上是调用了拦截器链,并通过拦截器一层层的递归,最终得到了请求返回Response,它的内容比较多具体在okhttp源码解析--拦截器中进行分析

    executed判断同一个http请求只能执行一次,否者直接抛出异常

    client.dispatcher().executed(this);
    

    直接调用dispatcherexecuted方法

     /** Used by {@code Call#execute} to signal it is in-flight. */
      synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    

    添加到dispatcherrunningSyncCalls同步请求队列中,通过getResponseWithInterceptorChain()拦截器链获得Request并在finally中释放当前请求。dispatcher中被调用的finished方法:

     /** Used by {@code AsyncCall#run} to signal completion. */
      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();
        }
      }
    

    在队列中移除请求,到此同步请求完毕。

    1.4 异步请求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));
      }
    

    首先和execute方法一样判断是否是第一次请求,captureCallStackTrace()获取网络请求的一些堆栈信息.最后一步才是真正的调用了dispatcher的上了同步锁enqueue方法,AsyncCall继承NamedRunnable就是一个runable

    /**
     * Runnable implementation which always sets its thread name.
     */
    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();
    }
    

    Dispatcher中的enqueue

    public final class Dispatcher {
      private int maxRequests = 64;
      private int maxRequestsPerHost = 5;
    
      synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

    首先判断异步执行中的队列是否已达到最大请求数量64&&线程数是否已到5条,没有则添加到runningAsyncCalls队列并通过线程池执行请求,否者添加到readyAsyncCalls等待队列.

    Dispatcher中的线程池

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

    这里核心线程数是0(核心线程设置为0的作用在于,当线程池没有核心线程空闲一段时间后会销毁所有的线程),最大线程数量是最大值(因为上面已经设置了maxRequests(64)了,60代表线程执行完毕后60秒会被销毁,所有不用担心会造成极端请求的情况)。最后 executorService().execute(call)就调用了当前线程池中子线程的run方法,也就是上图的NamedRunnable中的run(),其中NamedRunnable的核心抽象方法execute()在它的实现类AsyncCall中具体实现:

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
    
        AsyncCall(Callback responseCallback) {
          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 {
            client.dispatcher().finished(this);
          }
        }
      }
    

    通过getResponseWithInterceptorChain()拦截链获取到了请求返回Response
    retryAndFollowUpInterceptor.isCanceled()判断请求是否取消,
    取消就调用 responseCallbackonFailure(...)失败方法,
    否则responseCallbackonResponse(...)返回数据。这样一个异步请求就完成了。

    注意finally中的finished,通过它dispatcher维护了队列,线程池执行下一个网络请求,具体会在下一篇进行介绍

    相关文章

      网友评论

        本文标题:okhttp源码解析--同步|异步请求流程

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