OkHttp源码解析

作者: 奔跑吧李博 | 来源:发表于2020-09-10 09:37 被阅读0次

    Okhttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由大名鼎鼎的Square公司贡献。

    使用方式

    引入库:
     implementation 'com.squareup.okhttp3:okhttp:3.2.0'  
    
    发起get请求:
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(20, TimeUnit.SECONDS)
                    .build();
    
            Request request = new Request.Builder()
                    .url("")
                    .build();
            try {
                Response response = okHttpClient.newCall(request).execute();
                response.body().string();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    发起post请求:
           RequestBody requestBody = new FormBody.Builder()
                    .add("","")
                    .build();
    
            Request request = new Request.Builder()
                    .url("")
                    .addHeader("","")
                    .post(requestBody)
                    .build();
    
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(20, TimeUnit.SECONDS)
                    .build();
    
            try {
                Response response = okHttpClient.newCall(request).execute();
                response.body().string();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    okhttp内部封装功能:
    final Dispatcher dispatcher;
      final Proxy proxy;
      final List<Protocol> protocols;
      final List<ConnectionSpec> connectionSpecs;
      final List<Interceptor> interceptors;
      final List<Interceptor> networkInterceptors;
      final ProxySelector proxySelector;
      final CookieJar cookieJar;
      final Cache cache;
      final InternalCache internalCache;
      final SocketFactory socketFactory;
      final SSLSocketFactory sslSocketFactory;
      final TrustRootIndex trustRootIndex;
      final HostnameVerifier hostnameVerifier;
      final CertificatePinner certificatePinner;
      final Authenticator proxyAuthenticator;
      final Authenticator authenticator;
      final ConnectionPool connectionPool;
      final Dns dns;
      final boolean followSslRedirects;
      final boolean followRedirects;
      final boolean retryOnConnectionFailure;
      final int connectTimeout;
      final int readTimeout;
      final int writeTimeout;
    
    属性解释:
    • Dispatcher作为调度器,内部有线程池,负责调度同步请求和异步请求队列
    • Proxy 代理设置 相当于完全信任的中间代理
    • List<Protocol> protocols 支持的具体http协议版本,有http/1.0,http/1.1等
    • interceptors,networkInterceptors 这两个是okhttp中重要的拦截器
    • Cache cache缓存的存储配置,默认没有。如果需要用,自己填写存储文件位置以及存储空间大小
    • SocketFactory 使用默认的Socket工厂产生Socket,TCP传输层有关,三次握手
    • SSLSocketFactory 带上ssl的socket的工厂,就是整个https连接的过程
    • connectionPool 连接池
    • dns 域名解析器,根据域名获得IP地址
    • retryOnConnectionFailure 在请求失败后是否自动重试
    • connectTimeout 连接超时时间
    OkHttp流程图:
    流程解析
    创建OkhttpClient
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(20, TimeUnit.SECONDS)
                    .build();
    

    官方建议使用单例创建OkHttpClient,即一个进程中只创建一次即可,以后的每次交易都使用该实例发送交易。这是因为OkHttpClient拥有自己的连接池和线程池,这些连接池和线程池可以重复使用,这样做利于减少延迟和节省内存,如果咱们每次发交易都创建一个OkHttpClient的话,将会浪费很多内存资源。

    创建Call对象
            val request = Request.Builder()
                .url("")
                .build()
    
            okHttpClient.newCall(request)
    

    一个Call对象表示一次请求,每一次请求都会生成一个新的Call,Call其实是一个接口,它的具体实现类是RealCall。

      @Override public Call newCall(Request request) {
        return new RealCall(this, request);
      }
    
    RealCall构造方法:
      protected RealCall(OkHttpClient client, Request originalRequest) {
        this.client = client;
        this.originalRequest = originalRequest;
      }
    

    创建Call对象时候传进去了一个Request对象,Request对象表示用户的请求参数,并传入OkHttpClient对象。

    Request类部分代码:
    public final class Request {
      private final HttpUrl url;  //请求url
      private final String method;  //请求的get/post/put/delete方式
      private final Headers headers;  //请求头
      private final RequestBody body;  //请求体
      private final Object tag;
    
      private volatile URI javaNetUri; // Lazily initialized.
      private volatile CacheControl cacheControl; // Lazily initialized.
    
      private Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tag = builder.tag != null ? builder.tag : this;
      }
    }
    
    执行请求

    okhttp3中提供了两种请求方式:一种是同步请求,第二种是异步请求。同步请求调用call.execute()方法,异步请求调用call.enqueue(Callback callback)方法。

    public interface Call {
    
     /**立即调用请求,并阻塞,直到响应可以被处理或进入*/
      Response execute() throws IOException;
    
      /**
       * 计划在将来某个时间执行的请求
       */
      void enqueue(Callback responseCallback);
    }
    
    RealCall中实现:
    
      @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain(false);
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    
      @Override public void enqueue(Callback responseCallback) {
        enqueue(responseCallback, false);
      }
    
      void enqueue(Callback responseCallback, boolean forWebSocket) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
      }
    

    执行请求最终是通过Dispatcher类来调用,Dispatcher是okhttp3的任务调度核心类,负责管理同步和异步的请求,管理每一个请求任务的请求状态,并且其内部维护了一个线程池用于执行相应的请求。

    Dispatcher类部分代码:
    public final class Dispatcher {
      /** 用于保存等待执行的异步队列 */
      private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
      /** 用于保存正在执行的异步队列 */
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    }
    
     //调度器Dispatcher内部维护了一个ThreadPoolExecutor线程池,并直接将call对象传入线程池执行。
      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;
      }
       
      //同步执行
      synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
      
      //异步执行
      synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

    可以这么理解:把Dispatcher当成生产者,把线程池当成消费者,如果生产的线程小于可消费的范围,则立即加入消费队列;而当生产者生产的线程大于消费者所能承受的最大范围,就把未能及时执行的任务保存在readyAsyncCalls队列中,当时机成熟,也就是线程池有空余线程可以执行时,会调用promoteCall()这个方法把等待队列中的任务取出放到线程池中执行,并且把这个任务转移到runningAsyncCalls队列中去。

    拦截器链

    通过上面的分析咱们知道不管是同步请求还是异步请求,最后都会走getResponseWithInterceptorChain()方法,getResponseWithInterceptorChain()是okhttp3中的精髓设计之一。

    Response response = getResponseWithInterceptorChain(forWebSocket);
    

    具体实现过程:

      private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
        Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
        return chain.proceed(originalRequest);
      }
    
      class ApplicationInterceptorChain implements Interceptor.Chain {
        private final int index;
        private final Request request;
        private final boolean forWebSocket;
    
        ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
          this.index = index;
          this.request = request;
          this.forWebSocket = forWebSocket;
        }
    
        @Override public Connection connection() {
          return null;
        }
    
        @Override public Request request() {
          return request;
        }
    
        @Override public Response proceed(Request request) throws IOException {
          // If there's another interceptor in the chain, call that.
          if (index < client.interceptors().size()) {
            Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
            Interceptor interceptor = client.interceptors().get(index);
            Response interceptedResponse = interceptor.intercept(chain);
    
            if (interceptedResponse == null) {
              throw new NullPointerException("application interceptor " + interceptor
                  + " returned null");
            }
    
            return interceptedResponse;
          }
    
          // No more interceptors. Do HTTP.
          return getResponse(request, forWebSocket);
        }
      }
    

    获取okhttpClient中设置的各个intercepter拦截器,通过拦截器链对请求数据和返回数据进行处理,内部采用责任链模式,将每一个拦截器对应负责的处理任务进行严格分配,最后将交易结果返回并回调到暴露给调用者的接口上。

    参考文章
    okhttp3源码分析:架构全面解析

    相关文章

      网友评论

        本文标题:OkHttp源码解析

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