美文网首页
OkHttp探索记

OkHttp探索记

作者: Btwo | 来源:发表于2019-10-24 14:25 被阅读0次

    探索之前还是要走走流程的,步骤不能少,毕竟大家都这么写了,话不多说,先上使用步骤。

    使用步骤

    1.不管三七二十一先new对象

         OkHttpClient client =new OkHttpClient();

         Request request =new Request.Builder()

                                        .get()

                                        .url("https:www.baidu.com")

                                        .build();

         Call call = client.newCall(request);

    新建了3个对象,一个是OkHttpClient ,Request 还有Call。其中Request的方式用了builder模式,这里岔开个话题说说builder模式,学习一下,反正很敷衍,肯定避免不了跑题。

    说说我对builder模式敷衍的见解,构造的参数比较多,用builder模式就对了,你看看Request构造的参数,不少,用builder模式还有一个好处就是,代码清晰,一目了然,比那些把参数全都丢到构造函数的好几百倍,都不知道什么玩意。有一个不好的就是,到构成参数比较多的话,builder模式代码的幅度相对来讲究比较长了。

    2.有对象了那就得干活了

    同步请求

    直接执行call 对象的execute()

    try {

               Response response =  call.execute();

    }catch (IOException e) {

                e.printStackTrace();

    }

    就是这么简单粗暴。

    异步请求

    异步调用,并设置回调函数

    call.enqueue(new Callback() {

                @Override

                 public void onFailure(Call call, IOException e) {

                         Toast.makeText(MainActivity.this, "get failed", Toast.LENGTH_SHORT).show();

                       } 

    @Override

        public void onResponse(Call call, final Response response)throws IOException {

                             final String res = response.body().string();

                             runOnUiThread(new Runnable() {

                        @Override

                             public void run() {

                             }

                           });

        }

    });

    OkHttp的使用就敷衍的说到这吧,我觉得写得相当好,veryGood,接下来就随便看下里面的具体实现吧。

    OkHttp几个类的爱恨情仇藕断丝连

    还是老规矩,打开windows的画图工具(mac的自己想办法找个工具,我就不管了),来画一画,几个类的关系,

    推荐大家自己手动画图,画图可以帮助理解

    Okhttp几个关键类的关系

    看得懂不?RealCall继承了Call类,然后RealCall的实现依赖了Request和OkHttpClient,同时RealCall拥有Transmitter,Okhttp拥有RealInterceptorChain和dispater,简单来就是这样理解,那这几个类到底是怎么工作的呢,接下来就是探索这些了。

    Okhttp异步请求流程

    直接贴代码,异步是调用call.enqueue(Callback callback);现在就要看看enqueue做了什么,也就是上图画的RealCall方法enqueue做了什么。

    RealCall的enqueue的方法

    1.调用了transmitter的callStart(),其次调用client.dispatcher.enqueue(AsyncCall(responseCallback))

    2.Dispatcher的enqueue(AsyncCall(responseCallback))

    Dispatcher的enqueue

    call加入到readyAsyncCalls队列中去。然后查看一下当前是否存在相同域名的请求,如果存在同步下callsPerHost,callsPerHost的参数的意思是每个域名当前的请求次数,AtomicInteger,原子性。

    3.promoteAndExecute()

    promoteAndExecute()

                    遍历readyAsyncCalls的所有Call,判断当前正在执行的请求runningAsyncCalls队列的长度有没有超过最大的请求数 maxRequest,如果超过了直接break了,maxRequests默认是64,如果不超过最大的请求数,判断每一个域名同时请求的最大数是否超过maxRequestsPerHost,maxRequestsPerHost的默认值是5,如果超过了就continue,继续执行下一个ReaCall的遍历,两个值都没有超过,首先每个域名的请求数加1,也就是asyncCall.callsPerHost().incrementAndGet(),接着往executable和runningAsyncCalls队列分别加入这个Call。

    4.关键的一步asyncCall.executeOn(executorService)

       传入的参数是一个线程池executorService

    dispatcher的线程池

    核心线程数是0,最大线程数无限制。

    这里岔开话题说说线程池吧,要不也不知道一个请求的任务来了会咋样。

    corePollSize:核心线程

    maximumPoolSize:最大线程数

    keepAliveTime:空闲的线程保留的时间

    TimeUnit:空闲线程保留的时间单位

    BlockingQueue<Runnable>:阻塞队列,存储阻塞等待队列

    ThreadFactory:线程工厂,用来创建线程

    RejectedExecutionHandler:拒绝策略,当任务队列放不下,且已满

    这里的线程池的意思是:无核心线程,用了一个无缓冲的队列SynchronousQueue,充分利用空闲线程,SynchronousQueue入队的前提是有一个线程真正等待取队列的任务。

    5.RealCall的run方法。

    RealCall的run方法。

    这里看到了一个很重要的方法getResponseWithInterceptorChain(),执行一系列的拦截器链,这里就是Okhttp一系列的拦截器执行和责任链模式的体现。

    6.getResponseWithInterceptorChain()方法了解责任链模式

    Http拦截器

    调用链的触发chain.proceed(originalRequest),首先会执行自定义的本地拦截器,也就是client.interceptors这部分的东西,

    依次执行:RetryAndFollowUpInterceptor,BridgeInterceptor,CacheInterceptor,和一些网络的拦截器,最底层的拦截器就是CallServerInterceptor.当最底层的拦截器执行完之后,会向上传递Response,一步一步完成相关的操作。

    简单说说几个拦截器的意思和作用

    1.RetryAndFollowUpInterceptor

    顾名思义,这个拦截器的主要作用就是失败的时候重试,和重定向的作用,当用户取消的时候,会抛出IO异常。当请求失败的时候,会尝试去恢复,如果响应回来了会对响应码做解析,是否是需要代理验证的,或者是身份验证,还有就是重定向等。FollwUp的次数不同的浏览器也是不同的.

    2.BridgeInterceptor

    翻译就是桥拦截器?差不多啦,反正就是起了桥同样的作用,起了应用Request代码到框架网络层Request的代码的转换,和网络的响应转换成了用户上层的响应,GZip解压就是在这一层做的。

    3.CacheInterceptor

    缓存拦截器,主要是做两件事,一是从缓存中拿出数据响应给响应的Request,二是更新相应的请求进缓存。

    4.ConnectInterceptor

    打开一个连接连接到服务器,并传递到下一个拦截器,这个就是负责连接服务器的。

    5.CallServerInterceptor

    这个拦截器真正的做了请求服务器,也是拦截器里最底层的一个拦截器,在connectInterceptor打开了到服务器的连接之后,这个拦截器主要是往服务器写入相应的请求头,请求体,还有就是从服务器上读取响应头,读取响应体。然后往上回传Response

    比较复杂的几个知识点

    先google几个概念,Connection,Routes, Addresses, Urls,

    Connection,Okhttp使用三种类型访问服务器,Url,Address,Route

    Url,就是一个网络服务器主机的网址可以指定每个请求是明文的还是加密的。

    Address,Addresses指定网络服务器(如github.com)和所有的静态必要的配置,以及连接到改服务器的端口号,https的设置和首选的网络协议,为达到的目的是共享相同的TCP套接字连接,更低的延迟,更高的吞吐量。

    Routes:提供连接到一个网络服务器所必须的动态信息,就是尝试特定的IP地址,如DNS查询发现,使用准确的代理服务器。

    ConnectInterceptor拦截器是怎么做到复用连接的?连接池又是怎么样的?TLS连接的过程又是怎么样?Okhttp路由又是怎么样的?

    比较重要的一个类是Transmitter,这个类连接okhttp应用层到网络层,这个类支持异步的取消请求,当整个连接还处于TLS 握手过程,取消会取消所有的请求,不是处于TLS握手的过程中,当取消一个请求,不会取消和这个请求共用一个连接的其他请求。一切从ConnectInterceptor这个拦截器说起。ConnectInterceptor调用了Transmitter的newExchange

    Transmitter方法newExchage

    1.newExchage(chain,doExtensiveHealthChecks)返回一个exChange包含了新的Request和response。继续看方法exchangeFinder.find(client.chain,doExtensiveHealthChecks),发现有一个方法findHealthyConnection()

    2.方法findHealthyConnection(......),找到一个有用的连接,并返回,如果这个存在一个不可用的连接,就一直循环直到找到可用的连接

    3.真正调用exchangeFinder的方法 findConnection(connectTimeout: Int, readTimeout: Int,writeTimeout: Int,pingIntervalMillis: Int, connectionRetryEnabled: Boolean),返回一个当前请求的一个域名的连接,如果连接池存在的话直接返回,如果不存在的话就新建一个

    总结:当使用OkHttp进行Url请求,流程是:使用URL和配置的OkHttpClient创建一个Address,此地址指定我们如何连接到服务器,它通过地址从连接池中取回一个连接,如果它没有在池中找到连接,他会选择route尝试,这通常意味着使一个DNS请求,以获取服务器的IP地址,如果需要,它会选择一个TLS版本和代理服务器。一旦响应已经被接收到,该连接将会返回到池中,以便它可以在将来的请求中被重用,连接在池中一段时间后,它会被赶出

    这里只是简单的探索而已,后续有时间再深入看TLS连接的部分,和路由选择的部分

    相关文章

      网友评论

          本文标题:OkHttp探索记

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