美文网首页
OKHttp3源码阅读

OKHttp3源码阅读

作者: 河里的枇杷树 | 来源:发表于2017-07-17 11:06 被阅读45次

    OKHttp牛逼之处

    1.支持HTTP2/SPDY黑科技 --->okHttp中分包就分为Http1 和Http2

    2.socket支持自动重连 ---> 支持自动重连是因为他有RetryAndFollowIntercepter

    3.拥有自动维护的socket连接池,减少握手次数-->也就是ConnectionPool ,ConnectionPool 支持5个并发KeepAlive,默认链路生命为5分钟

    //ConnectionPool 中
    public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
    }
    
    public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
    
    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
    }
    

    4.拥有队列线程池,轻松写并发 -->Dispatcher

    5.拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)--->在AsyncCall中的execute()中会调用getResponseWithInterceptorChain()会处理这一系列的Interceptors

    6.基于Headers的缓存策略-->因为有CacheInterceptor

    OKHttp3中类的作用

    0.Interceptor;(接口)拦截器,将请求进行拦截,并对其头文件进行修改或删除,内部有一个Chain,也是一个接口

    1.CacheIntercepter:(实现了Interceptor)从缓存中查找请求或者将请求写入到缓存中.

    2.CacheStrategy:(缓存策略)根据给定的条件选择 OKHttp是否使用网络请求,或者直接使用缓存或者都是用.

    3.FaultHidingSink:对流操作进行了封装和try/cache,可以保证对外界不抛出IOException.

    4.FileOperator:文件的读写类

    5.ConnectionInterceptor:(实现了Interceptor)负责和服务器进行连接的拦截器

    6.ConnectionSpecSelector:处理连接规范回退策略: 当由于握手/协议问题导致安全套接字连接失败时,可能会使用不同的协议重试连接。实例是有状态的,应该被创建并用于单个连接尝试。

    7.RealConnection:真正使用Http协议去连接网络的类

    8.RouteDatabase:记录创建失败的路由

    9.RouteSelector:负责线路(路由)的选择和自动重连

    10.StreamAllocation:主要负责Connections和Stream的资源分配和释放

    11.BridgeInterceptor:(实现了Interceptor接口)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应

    12.CallServerInterceptor:(实现了Interceptor接口)他是调用连中的最后一个拦截器,用于发送网络请求到服务器中

    13.HttpCodec:(接口)用于编码Http请求和解码Http响应的父级接口

    14.HttpDate:Http的日期解析器

    15.HttpHeaders:OkHttp内部封装的请求头

    16.RealInterceptorChain:实现了Interceptor中内部接口Chain的类.真正的拦截器链,调用完一个拦截器在调用下一个拦截器

    17.RealResponseBody:(继承了ResponseBody) OkHTTP中对responseBody的封装

    18.RetryAndFollowInterceptor:(实现了Interceptor接口)负责重试和重定向的拦截器,如果取消有可能抛出IOException异常

    19.RequestLine:请求行

    20.StatusLine:HTTP响应状态栏

    21.HPACK:Http/2的头部压缩算法

    22.Huffman:霍夫曼编码

    23.Ping:本地发起的ping

    24.Cache:将http和https的响应使用DiskLruCache缓存到文件夹系统中

    25.CacheControl:一个缓存控制头,带有来自服务器或客户端的缓存指令。这些指令规定了哪些响应可以被存储,哪些请求可以被存储的响应满足

    26.Call:(接口)请求任务的封装(一个Call只能执行一次)

    27.RealCall:Call的实现类

    28.CertificatePinner:约束那些证书是可信的

    29.Connection:(接口)RealConnection实现了它,按照各种协议去请求网络的接口

    30ConnectionPool:Socket连接池,对连接缓存进行回收和管理

    31.Dispatcher:用于线程分发,它有两种方法,一个是普通的同步单线程;另一种是使用了队列进行并发任务的分发(Dispatch)与回调

    32.OkHttpClient:是Call的工厂

    32.Request:一个Http请求的封装,包含一个URL、一个方法(GET或POST或其他)、一些HTTP头。请求还可能包含一个特定内容类型的数据类的主体部分。

    33.Response:Http响应的封装,包含状态码、HTTP头和主体部分。

    35.HttpUrl:对Url的封装,包含http和https

    最简单的get请求访问网络代码解析

        OkHttpClient okHttpClient = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        builder.url("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png");
        builder.method("GET", null);
        final Request request = builder.build();
        Call newCall = okHttpClient.newCall(request);
        newCall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
    
            }
    
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                boolean b = response.cacheResponse() != null;
                if (BuildConfig.DEBUG) Log.e("MainActivity", "b:" + b);
    
                InputStream inputStream = response.body().byteStream();
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
    
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mIv.setImageBitmap(bitmap);
    
                    }
                });
            }
        });
    

    1.首先在new OKHttpClient()的时候,会调用自己的OkHttpClient(Builder builder)这个构造方法,将一个builder传进去,Builder是OkHttpClient的静态内部类,创建Builder的时候,也就是进行了一大堆的初始化操作,然后将builder传到OKHttpClient的构造方法中,也就是将builder中初始化的参数赋值给OKHttpClient中

    //Builder的构造方法
    public Builder() {
      //初始化线程分发器
      dispatcher = new Dispatcher();
      //初始化协议包括,Http_1_1和Http_2
      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;
    }
    
    //OkHttpClient最终走的构造方法
    OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;
    
    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }
    
    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }
    
    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
    }
    

    2.在创建Request.Builder的时候,首先在Builder的构造方法中给Builder设置了默认的请求方式为"GET"和一个空的Headers.Builder()对象

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }
    

    3.然后我们来看看下面这两句代码

        builder.url("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png");
        builder.method("GET", null);
    

    首先我们给builder设置了url,不过是post还是get请求url总是必不可少的,然后有设置了请求方式为"GET"我们在上面已经知道了,OkHttp默认的请求方法就是"GET"所以如果我们的请求方式是"GET"的话可以不再设置.

    4.下来 Request request = builder.build(); 这句代码做的工作和OKHttpClient内部做的工作一样,就是将builder中携带的参数,赋值给Request,这也就是明显的建造者模式

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
    
    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;
    }
    

    5.下来Call newCall = okHttpClient.newCall(request);这句代码是创建一个Call(一个请求的封装),在执行newCall的时候会创建一个RealCall的实例,在创建RealCall的时候,会将OKHttpClient,Request,EventListener(该次请求的监听),和请求失败以后的拦截器RetryAndFollowInterceptor()这些进行初始化,并记录在RealCall中

        @Override 
        public Call newCall(Request request) {
        return new RealCall(this, request, false /* for web socket */);
        }
    
        RealCall(OkHttpClient client, Request    originalRequest, boolean forWebSocket) {
        final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
        //OKHttpClient
        this.client = client;
        //一个Request
        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);
        }
    

    6.最后就是执行newCall.enqueue(new Callback() )这个方法,这个方法就是去执行请求了,然后通过CallBack将结果回调回来.在执行enqueue方法的时候首先会通过RealCall身上的executed判断该请求是否执行过了,如果执行过了就会抛异常,从这里就可以看出一个请求只能被执行一次.然后就执行了Dispatcher的enqueue()方法,传入了一个AsyncCall对象,AsyncCall 是继承了NamedRunnable类,NamedRunnable又继承了Runnable,所以说AsyncCall就是一个线程.

    @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    

    在Despatcher的enqueue()方法中首先会判断目前正在执行的请求数如果不大于最大请求数(64个)并且将要执行的这个请求的主机名 和正在执行的请求的主机名相同的请求数不超过5,就将该请求执行并且添加都正在执行请求的队列中,否则就将该请求添加到等待执行的队列中.

    synchronized void enqueue(AsyncCall call) {
      if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
        runningAsyncCalls.add(call);
        executorService().execute(call);
      } else {
        readyAsyncCalls.add(call);
      }
     }
    

    在执行executorService()这个方法的时候会返回一个线程池,也就是OKHttp访问服务器的线程池,添加到线程池中以后就标志着这个请求将要被执行.我们可以很明显的看出,这个线程池是没有核心线程数的,而且最大线程是int的最大值(也就是灰常大),线程在空闲60秒后也就是一分钟后销毁.

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

    该请求被执行的话肯定会走自己的run()方法,所以我们来看看AsyncCall的run方法,run方法中只执行了execute()方法,而且该方法是抽象的,所以我们就来看看AsyncCall的execute方法.

    @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
    }
    

    在execute()中首先会执行getResponseWithInterceptorChain()在这个方法中会将之前各个地方的拦截器全部统一撞到一个集合中,然后创建一个RealInterceptorChain,并将这个拦截器的集合作为参数穿进去,最后会执行RealInterceptorChain的Proceed方法去执行各个Interceptor然后最后获得一个Response,这里是真正访问网络的地方.合适最核心的地方,下面 我们来具体看看到底是怎么弄得.

    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);
    //开始递归执行每一个Interceptor
    return chain.proceed(originalRequest);
     }
    

    按照Interceptor添加的顺序首先会执行执行OKHttpClient配置的Interceptor,但也有可能没有配置,所以智力我们先不管.

    下来是RetryAndFollowUpInterceptor,RetryAndFollowUpInterceptor类中我们主要看他的intercept方法,该方法中主要是对请求失败的请求进行重试

    再下来是BridgeInterceptor,BridgeInterceptor中的intercept方法主要是使用Request.Builder将用户创建的Request组装成真正发给服务器的请求包,也是用Request.Builder将服务器返回的结果封装成,我们需要的Response

    再下来就是CacheInterceptor,CacheInterceptor中的intercept方法主要是到缓存中找,是否有已经有缓存而且没有过期就直接使用缓存中的Response,如果没有的话就进行下一个Interceptor

    下一个Interceptor是ConnectInterceptor,ConnectInterceptor的intercept方法中主要是和服务器进行链接

    最后一个Interceptor就是CallServerInterceptor,它主要是将请求提交给服务器,然后将结果进行返回.

    最后我们来总结一下这些Interceptors的执行顺序和各自的功能

    image.png

    7.在getResponseWithInterceptorChain执行完以后就会返回一个Response到RealCall的execute方法中,然后使用我们调用层传入的Callback将结果回调到调用层,因为这里没有做线程的操作,所以onFailure()和onResponse()都是运行在子线程中的

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

    总体流程

    (图1)

    OKHttp访问网络流程.png

    (图2)

    image.png

    细节解析

    1.OKHttp访问网络的线程池是如何构建的?
    OKHttp的线程池没有核心线程,而且最大线程是Int的最大值.在空闲60s后自动销毁,而且使用的是SynchronousQueue这个不储存元素的阻塞队列,也就是说来一个任务就会来一个线程.

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

    2,Dispatcher线程池总结

    1)调度线程池Disptcher实现了高并发,低阻塞的实现

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

    2)采用Deque作为缓存,先进先出的顺序执行

    /** 准备执行的请求 */
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
    /** 正在执行的异步请求,包含已经取消但未执行完的请求 */
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
    /** 正在执行的同步请求,包含已经取消单未执行完的请求 */
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    

    3)任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能

    //RealCall的AsyncCall中
    @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);
      }
    }
    

    3.OKHttp中再执行Interceptor这块使用了责任链模式,有点像view中的事件传递分发

    4.OKHttp是直接使用socket和服务器进行通信的

    5.OKHttp需要打印日志的话 需要添加日志拦截器 如下:

    compile 'com.squareup.okhttp3:logging-interceptor:3.1.2'
    

    参考文章

    http://blog.csdn.net/mwq384807683/article/details/71173442?locationNum=8&fps=1

    http://www.jianshu.com/p/aad5aacd79bf

    相关文章

      网友评论

          本文标题:OKHttp3源码阅读

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