OkHttp探索记

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

使用步骤

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连接的部分,和路由选择的部分

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 前言 用OkHttp很久了,也看了很多人写的源码分析,在这里结合自己的感悟,记录一下对OkHttp源码理解的几点心...
    Java小铺阅读 1,518评论 0 13
  • 这篇文章主要讲 Android 网络请求时所使用到的各个请求库的关系,以及 OkHttp3 的介绍。(如理解有误,...
    小庄bb阅读 1,159评论 0 4
  • 用OkHttp很久了,也看了很多人写的源码分析,在这里结合自己的感悟,记录一下对OkHttp源码理解的几点心得。 ...
    蓝灰_q阅读 4,279评论 4 34
  • 今天,我要完成一项特殊的任务,那就是护蛋。 我从冰箱里挑了一个比较可爱的鸡蛋,为了防止鸡蛋碎了以后,蛋...
    卿子函阅读 419评论 0 0
  • 柠檬框架 -- 基于去涩留精的架构理念实现代码的最优化。 实现方案:springboot + amqp (rabb...
    龙佚阅读 1,829评论 0 1