文档章节

OkHttp完全解析(九)源码解析二

k
 kim366
发布于 2016/05/13 19:08
字数 1250
阅读 5
收藏 0
点赞 2
评论 0

上一篇文章里我们大致分析了OkHttp整个请求的流程,重点分析了具体发送请求前都做了哪些操作,这篇文章我们将继续上篇的内容,看看在发送请求过程中做了什么,看了上篇文章的应该都知道,我们将从HttpEngine的sendRequest入手看是如何操作的

publicvoid sendRequest() throws RequestException, RouteException, IOException {
        if(this.cacheStrategy == null) {
            if(this.transport != null) {
                thrownew IllegalStateException();
            } else {
                Request request = this.networkRequest(this.userRequest);
                //读取用户设置的缓存
                InternalCache responseCache = Internal.instance.internalCache(this.client);
                //从缓存中读取之前相同请求得到的Response
                Response cacheCandidate = responseCache != null?responseCache.get(request):null;
                long now = System.currentTimeMillis();
                //根据请求和缓存结果(可能为null)去得到缓存策略this.cacheStrategy = (new Factory(now, request, cacheCandidate)).get();
                //如果请求策略为只要缓存则networkRequest,cacheResponse都为空,大部分条件都会得到networkRequest就是request,cacheResponse为空this.networkRequest = this.cacheStrategy.networkRequest;
                this.cacheResponse = this.cacheStrategy.cacheResponse;
                if(responseCache != null) {
                    responseCache.trackResponse(this.cacheStrategy);
                }

                if(cacheCandidate != null && this.cacheResponse == null) {
                    Util.closeQuietly(cacheCandidate.body());
                }

                if(this.networkRequest != null) {
                    if(this.connection == null) {
                        //建立连接this.connect();
                    }
                    //得到HttpTransport(Http请求)this.transport = Internal.instance.newTransport(this.connection, this);
                    //根据条件将一些执行请求头部的写入,具体的写入会调动到httpConnection的sink(就是一个socket请求的outputstream,具体后面分析)去writeif(this.callerWritesRequestBody && this.permitsRequestBody() && this.requestBodyOut == null) {
                        long contentLength = OkHeaders.contentLength(request);
                        if(this.bufferRequestBody) {
                            if(contentLength > 2147483647L) {
                                thrownew IllegalStateException("Use setFixedLengthStreamingMode() or setChunkedStreamingMode() for requests larger than 2 GiB.");
                            }

                            if(contentLength != -1L) {
                                this.transport.writeRequestHeaders(this.networkRequest);
                                this.requestBodyOut = new RetryableSink((int)contentLength);
                            } else {
                                this.requestBodyOut = new RetryableSink();
                            }
                        } else {
                        //进入这个判断说明不需要走网络请求,直接读取缓存 this.transport.writeRequestHeaders(this.networkRequest);
                            this.requestBodyOut = this.transport.createRequestBody(this.networkRequest, contentLength);
                        }
                    }
                } else {
                    if(this.connection != null) {
                        Internal.instance.recycle(this.client.getConnectionPool(), this.connection);
                        this.connection = null;
                    }

                    if(this.cacheResponse != null) {
                        //组装缓存数据成Responsethis.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build();
                    } else {
                        //只要缓存的数据但之前又没有缓存则抛出 504的Responsethis.userResponse = (new Builder()).request(this.userRequest).priorResponse(stripBody(this.priorResponse)).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(EMPTY_BODY).build();
                    }
                    //解压请求结果this.userResponse = this.unzip(this.userResponse);
                }

            }
        }
    }

上面的代码先是针对请求策略去判断是否走网络,若不走网络networkResponse为空直接去生成userReponse,否则就进入网络请求状态并且在第28行进行建立连接操作,这个操作比较重要,我们进去看看

privatevoid connect() throws RequestException, RouteException {
        if(this.connection != null) {
            thrownew IllegalStateException();
        } else {
            if(this.routeSelector == null) {
            //建立一个Address,用来记录web服务器,以及要连接服务器需要的一些静态配置比如端口号,网络协议等this.address = createAddress(this.client, this.networkRequest);

                try {
                  //得到路由选择器,用于记录连接服务器的一些动态配置,比如查询DNS的ip,代理服务器,TLS协议版本this.routeSelector = RouteSelector.get(this.address, this.networkRequest, this.client);
                } catch (IOException var2) {
                    thrownew RequestException(var2);
                }
            }
            //得到httpConnectionthis.connection = this.nextConnection();
            this.route = this.connection.getRoute();
        }
    }
    private Connection nextConnection() throws RouteException {
        Connection connection = this.createNextConnection();
        //将connection设置到okhttpclient
        Internal.instance.connectAndSetOwner(this.client, connection, this, this.networkRequest);
        return connection;
    }

    private Connection createNextConnection() throws RouteException {
    //拿到连接池,如果用户没有设置pool,将得到默认的ConnectionPool 
        ConnectionPool pool = this.client.getConnectionPool();

        Connection e;
        //根据address和存活时间等条件找到是否有之前的connection可用while((e = pool.get(this.address)) != null) {
            if(this.networkRequest.method().equals("GET") || Internal.instance.isReadable(e)) {
                return e;
            }

            Util.closeQuietly(e.getSocket());
        }

        try {
            Route e1 = this.routeSelector.next();
            //没有满足条件的Connection建立新的connectionreturnnew Connection(pool, e1);
        } catch (IOException var3) {
            thrownew RouteException(var3);
        }
    }

上面的代码是跟服务器建立链接的过程,在createNextConnection里先去线程池里找是否有之前请求过该Address且还在存活时间里的connection否则新建一个,并调用Internal.instance.connectAndSetOwner(this.client, connection, this, this.networkRequest);,这个代码最后会执行到Connection里的

void connect(int connectTimeout, int readTimeout, int writeTimeout, Request request, List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) throws RouteException {
        if(this.connected) {
            thrownew IllegalStateException("already connected");
        } else {
        //建立Socket连接器
            SocketConnector socketConnector = new SocketConnector(this, this.pool);
            ConnectedSocket connectedSocket;
            if(this.route.address.getSslSocketFactory() != null) {
                connectedSocket = socketConnector.connectTls(connectTimeout, readTimeout, writeTimeout, request, this.route, connectionSpecs, connectionRetryEnabled);
            } else {
                if(!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
                    thrownew RouteException(new UnknownServiceException("CLEARTEXT communication not supported: " + connectionSpecs));
                }
                //建立socket并进行socket.connect发起对服务器的连接
                connectedSocket = socketConnector.connectCleartext(connectTimeout, readTimeout, this.route);
            }

            this.socket = connectedSocket.socket;
            this.handshake = connectedSocket.handshake;
            //这里我们最终会得到protocol为Protocol.HTTP_1_1this.protocol = connectedSocket.alpnProtocol == null?Protocol.HTTP_1_1:connectedSocket.alpnProtocol;

            try {
                if(this.protocol != Protocol.SPDY_3 && this.protocol != Protocol.HTTP_2) {
                //建立httpConnectionthis.httpConnection = new HttpConnection(this.pool, this, this.socket);
                } else {
                    this.socket.setSoTimeout(0);
                    this.spdyConnection = (new Builder(this.route.address.uriHost, true, this.socket)).protocol(this.protocol).build();
                    this.spdyConnection.sendConnectionPreface();
                }
            } catch (IOException var10) {
                thrownew RouteException(var10);
            }

            this.connected = true;
        }
    }

上面26行处我们建立的httpconnection,它的构造函数将根据传入的socket生成source(socket的输入流用来读)和s**ink(socket的输出流用来写)**

public HttpConnection(ConnectionPool pool, Connection connection, Socket socket) throws IOException {
        this.pool = pool;
        this.connection = connection;
        this.socket = socket;
        this.source = Okio.buffer(Okio.source(socket));
        this.sink = Okio.buffer(Okio.sink(socket));
    }
代码看到这里我们知道了connect的整个代码流程,得到socket,连接,建立输入输出流。

然后我们继续回到sendRequest看看connect后做了什么,sendRequest()代码31行新建HttpEngine,然后在32行处的if判断,我们可以从OKHttp源码解析(一) 文章末尾的代码22行处看到传入的callerWritesRequestBody为false(第四个参数),requestBodyOut为null(最后一个参数),因此这个if判断在这里并不会执行。 
分析到这里我们基本把整个发送请求的过程分析完了,又是大段大段贴代码,因为这部分其实逻辑比较清楚,代码也比较容易看懂,重要的部分都注释出来,下一篇我们将继续来讲解readResponse。

© 著作权归作者所有

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
OkHttp3源码解析内部缓存

OkHttp3系列文章 OkHttp3 源码解析执行流程 OkHttp3 源码解析 连接池的复用 如果有了解过OkHttp的执行流程,可以知道,在拦截器链中有一个缓存拦截器CacheInterceptor,里面决定了是由缓存中...

Gillben ⋅ 05/22 ⋅ 0

说说在 Android 中如何发送 HTTP 请求

客户端会向服务器发出一条 HTTP 请求,服务器收到请求后会返回一些数据给客户端,然后客户端再对这些数据进行解析与处理。 1 HttpURLConnection 可以使用 HttpURLConnection(官方推荐) 来发...

deniro ⋅ 06/09 ⋅ 0

Retrofit源码分析之OKHttpCall

之前在Retrofit源码初探一文中我们提出了三个问题: 什么时候开始将注解中参数拼装成http请求的信息的? 如何产生发起http请求对象的? 如何将对象转换成我们在接口中指定的返回值的? 其中第...

低情商的大仙 ⋅ 05/13 ⋅ 0

Android逆向之旅---爆破一款资讯类应用「最右」防抓包策略原理分析

一、逆向分析 首先感谢王同学提供的样本,因为王同学那天找到我咨询我说有一个应用Fiddler抓包失败,其实对于这类问题,我一般都会这么回答:第一你是否安装Fiddler证书了,他说他安装了。第...

jiangwei0910410003 ⋅ 04/25 ⋅ 0

Android Retrifot2简单使用学习

简介 Retrofit是一个现在比较火的网络请求框架,它的底层是依靠okhttp实现的(okhttp也是一款很不错的框架之前有使用过),确切的讲,Retrofit是对okhttp的进一步封装,它功能强大,支持同步...

reggie1996 ⋅ 05/27 ⋅ 0

spring传统xml配置okhttp3

问题 后端服务需要使用http客户端请求其他服务支持,项目中需要将HttpClient换成OKhttp,为啥要换OKhttp?这里不讨论这两者之间的优缺点。这篇文章主要关注与Spring传统xml配置方式集成Okhtt...

亚林瓜子 ⋅ 04/19 ⋅ 0

Android面试的那些答不上来的问题(一)--- OkHttp的拦截器你到底了解多少(上)

前言 前段时间面试了很多家公司(坐标成都,大大小小加起来得20家吧),有时候有些事做多了,你就会发现它的一些窍门或者规律,面试这件事当然也不例外。其实很多公司问到的问题都大同小异,...

SillyMonkey ⋅ 05/10 ⋅ 0

BAT大厂APP架构演进实践与优化之路

第1章 打车课程项目整体介绍 课程介绍:介绍课程内容、教授方式,讲诉打车业务的整体架构思路;针对打车业务如何进行需求分析和工作量化评估,同时根据工作量化提出课程内容章节计划。 第2章...

13269051240 ⋅ 05/21 ⋅ 0

Android面试有迹可循(一)OkHttp3.9拦截器原理与区别

接上回 传送门 上回我们讲到,OkHttp的请求过程中有个非常重要的东西-“拦截器”,而且拦截器又分为interceptors和networkInterceptors两种,那它们具体有何区别呢?又要怎么来使用?现在来一...

SillyMonkey ⋅ 05/19 ⋅ 0

OkHttp源码学习系列一:总流程和Dispatcher分析

本文为本人原创,转载请注明作者和出处。 OkHttp可以说是目前Android开发中最流行的基础网络框架了。相信你也一定早已学会了它的基本用法,今天我们来进一步学习它的源码,了解其请求原理,学...

业松 ⋅ 05/10 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 18分钟前 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

楠木楠 ⋅ 30分钟前 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 59分钟前 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 今天 ⋅ 0

Docker Toolbox Looks like something went wrong

Docker Toolbox 重新安装后提示错误:Looks like something went wrong in step ´Checking if machine default exists´ 控制面板-->程序与应用-->启用或关闭windows功能:找到Hyper-V,如果处......

随你疯 ⋅ 今天 ⋅ 0

Guacamole 远程桌面

本文将Apache的guacamole服务的部署和应用,http://guacamole.apache.org/doc/gug/ 该链接下有全部相关知识的英文文档,如果水平ok,可以去这里仔细查看。 一、简介 Apache Guacamole 是无客...

千里明月 ⋅ 今天 ⋅ 0

nagios 安装

Nagios简介:监控网络并排除网络故障的工具:nagios,Ntop,OpenVAS,OCS,OSSIM等开源监控工具。 可以实现对网络上的服务器进行全面的监控,包括服务(apache、mysql、ntp、ftp、disk、qmail和h...

寰宇01 ⋅ 今天 ⋅ 0

AngularDart注意事项

默认情况下创建Dart项目应出现以下列表: 有时会因为不知明的原因导致列表项缺失: 此时可以通过以下步骤解决: 1.创建项目涉及到的包:stagehand 2.执行pub global activate stagehand或pub...

scooplol ⋅ 今天 ⋅ 0

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部