文档章节

OkHttp完全解析(十)源码解析三

k
 kim366
发布于 2016/05/13 19:08
字数 1126
阅读 8
收藏 0
点赞 2
评论 0
publicvoid readResponse() throws IOException {
        if(this.userResponse == null) {
            if(this.networkRequest == null && this.cacheResponse == null) {
                thrownew IllegalStateException("call sendRequest() first!");
            } elseif(this.networkRequest != null) {
                Response networkResponse;
                if(this.forWebSocket) {
                    this.transport.writeRequestHeaders(this.networkRequest);
                    networkResponse = this.readNetworkResponse();
                } elseif(!this.callerWritesRequestBody) {
                //利用拦截器方式去做答复处理
                    networkResponse = (new HttpEngine.NetworkInterceptorChain(0, this.networkRequest)).proceed(this.networkRequest);
                } else {
                    //这个else分句可以不看
                }
                this.receiveHeaders(networkResponse.headers());
                if(this.cacheResponse != null) {
                    if(validate(this.cacheResponse, networkResponse)) {
                        this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).headers(combine(this.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
                        networkResponse.body().close();
                        this.releaseConnection();
                        InternalCache responseCache1 = Internal.instance.internalCache(this.client);
                        responseCache1.trackConditionalCacheHit();
                        responseCache1.update(this.cacheResponse, stripBody(this.userResponse));
                        this.userResponse = this.unzip(this.userResponse);
                        return;
                    }

                    Util.closeQuietly(this.cacheResponse.body());
                }

                this.userResponse = networkResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
                if(hasBody(this.userResponse)) {
                    this.maybeCache();
                    this.userResponse = this.unzip(this.cacheWritingResponse(this.storeRequest, this.userResponse));
                }

            }
        }
    }

在上篇文章结尾OKHttp源码解析(二) 我们分析到这里第10行的callerWritesRequestBody会为空,所以我会直接进去这个判断,同样在OKHttp源码解析(一) 我们分析过发送请求时使用的拦截器模式,这里对答复的操作也用了同样的方式,不同于请求调用的是intercept,这里用的是proceed,我们就来看看这个方法做了什么

public Response proceed(Request request) throws IOException {
            ++this.calls;
            if(this.index > 0) {
                Interceptor response = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index - 1);
                Address code = this.connection().getRoute().getAddress();
                if(!request.url().getHost().equals(code.getUriHost()) || Util.getEffectivePort(request.url()) != code.getUriPort()) {
                    throw new IllegalStateException("network interceptor " + response + " must retain the same host and port");
                }

                if(this.calls > 1) {
                    throw new IllegalStateException("network interceptor " + response + " must call proceed() exactly once");
                }
            }

            if(this.index < HttpEngine.this.client.networkInterceptors().size()) {
            //根据拦截器的数目去相应取出拦截器并执行intercept里面用户自定义的处理方式
                HttpEngine.NetworkInterceptorChain var7 = HttpEngine.this.new NetworkInterceptorChain(this.index + 1, request);
                Interceptor var10 = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index);
                Response interceptedResponse = var10.intercept(var7);
                if(var7.calls != 1) {
                    throw new IllegalStateException("network interceptor " + var10 + " must call proceed() exactly once");
                } else {
                    return interceptedResponse;
                }
            } else {
            //写入请求头部
                    HttpEngine.this.transport.writeRequestHeaders(request);
                HttpEngine.this.networkRequest = request;
                if(HttpEngine.this.permitsRequestBody() && request.body() != null) {
                //写入一些请求体
                    Sink var5 = HttpEngine.this.transport.createRequestBody(request, request.body().contentLength());
                    BufferedSink var8 = Okio.buffer(var5);
                    request.body().writeTo(var8);
                    var8.close();
                }
                //将之前写入的数据flush给socket并读取服务器答复
                Response var6 = HttpEngine.this.readNetworkResponse();
                int var9 = var6.code();
                if((var9 == 204 || var9 == 205) && var6.body().contentLength() > 0L) {
                    throw new ProtocolException("HTTP " + var9 + " had non-zero Content-Length: " + var6.body().contentLength());
                } else {
                    return var6;
                }
            }
        }

我们先来看看27行的头部写入是怎么一个写法

public void writeRequestHeaders(Request request) throws IOException {
        this.httpEngine.writingRequestHeaders();
        //组装请求的信息,比如url,请求方式,请求协议
        String requestLine = RequestLine.get(request, this.httpEngine.getConnection().getRoute().getProxy().type(), this.httpEngine.getConnection().getProtocol());
        //将请求头和请求体写入socket
        this.httpConnection.writeRequest(request.headers(), requestLine);
    }

public void writeRequest(Headers headers, String requestLine) throws IOException {
        if(this.state != 0) {
            throw new IllegalStateException("state: " + this.state);
        } else {
            this.sink.writeUtf8(requestLine).writeUtf8("\r\n");
            int i = 0;

            for(int size = headers.size(); i < size; ++i) {
                this.sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("\r\n");
            }

            this.sink.writeUtf8("\r\n");
            this.state = 1;
        }
    }

上面的sink就是socket的写入流,在上篇文章我们分析过怎么得到它的,将请求头部和请求体写入socket后,proceed的第37行就要进行flush操作了

private Response readNetworkResponse() throws IOException {
    //执行flush操作
        this.transport.finishRequest();
        //等待服务器相应并读取服务器返回信息组装成我们需要的response
        Response networkResponse = this.transport.readResponseHeaders().request(this.networkRequest).handshake(this.connection.getHandshake()).header(OkHeaders.SENT_MILLIS, Long.toString(this.sentRequestMillis)).header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())).build();
        if(!this.forWebSocket) {
        //组装response的body
            networkResponse = networkResponse.newBuilder().body(this.transport.openResponseBody(networkResponse)).build();
        }

        Internal.instance.setProtocol(this.connection, networkResponse.protocol());
        return networkResponse;
    }

先简单看下第三行finishRequest调用的代码

publicvoid finishRequest() throws IOException {
        this.httpConnection.flush();
    }
publicvoid flush() throws IOException {
        this.sink.flush();
    }

再来看看第5行的组装步骤

public Builder readResponseHeaders() throws IOException {
        return this.httpConnection.readResponse();
    }

public Builder readResponse() throws IOException {
        if(this.state != 1 && this.state != 3) {
            throw new IllegalStateException("state: " + this.state);
        } else {
            try {
                StatusLine e;
                Builder exception1;
                do {
                //从输入流里读出答复并组装成答复消息
                    e = StatusLine.parse(this.source.readUtf8LineStrict());
                    exception1 = (new Builder()).protocol(e.protocol).code(e.code).message(e.message);com.squareup.okhttp.Headers.Builder headersBuilder = new com.squareup.okhttp.Headers.Builder();
                    //答复头部的读取
                    this.readHeaders(headersBuilder);
                    headersBuilder.add(OkHeaders.SELECTED_PROTOCOL, e.protocol.toString());
                    exception1.headers(headersBuilder.build());
                } while(e.code == 100);

                this.state = 4;
                return exception1;
            } catch (EOFException var4) {
                IOException exception = new IOException("unexpected end of stream on " + this.connection + " (recycle count=" + Internal.instance.recycleCount(this.connection) + ")");
                exception.initCause(var4);
                throw exception;
            }
        }
    }

上面的代码我们看到的是对答复头部的读取整理,而readNetworkResponse()第8行则是对服务器答复的body进行整理组装

public ResponseBody openResponseBody(Response response) throws IOException {
        Source source = this.getTransferStream(response);
        //最终返回一个输入流returnnew RealResponseBody(response.headers(), Okio.buffer(source));
    }

    private Source getTransferStream(Response response) throws IOException {
        if(!HttpEngine.hasBody(response)) {
            returnthis.httpConnection.newFixedLengthSource(0L);
        } elseif("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
            returnthis.httpConnection.newChunkedSource(this.httpEngine);
        } else {
            long contentLength = OkHeaders.contentLength(response);
            return contentLength != -1L?this.httpConnection.newFixedLengthSource(contentLength):this.httpConnection.newUnknownLengthSource();
        }
    }

在经过这么层层代码的深入(好多代码,看得都乱了吧),我们最终得到了服务器返回的userResponse。。

最后的最后,我们要回到OKHttp源码解析(一) 最后一段代码getResponse的52行得到我们上面分析的userResponse,并关闭相应的连接池,关闭回收socket等”善后“处理。

三篇文章分析到现在,算是把整个OKHttp的流程分析了个大概,OkHttp还有其他的一些部分,比如handshake,连接池的管理等方面的内容,如果有人有发现这方面介绍的好文章可以留言推荐给我看看,大家共同学习,共同进步。

本文转载自:http://blog.csdn.net/oyangyujun/article/details/50040007

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
手写Android网络访问框架OkHttp(简易版)

开篇废话 趁着周末两天的时间,跟着大神的脚步,把我们经常使用的网络框架OkHttp的源码好好跟了一下,初次观看,确实非常容易钻进去,搞得云里雾里,在大神的指导下,才勉强把整个逻辑走通。...

进击的欧阳
05/06
0
0
Android中流行的第三方库资源收集

1、GSON GSON是将JSON解析成POJO的Java库。GSON也可以将POJO解析成JSON。 2、OkHttp OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存。默认情况下,OKHttp会自动...

jdroid
2014/05/02
0
0
说说在 Android 中如何发送 HTTP 请求

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

deniro
06/09
0
0
OkHttp3源码解析内部缓存

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

Gillben
05/22
0
0
使用Android API最佳实践

本文由 伯乐在线 - imesong 翻译自 meetme。欢迎加入Android小组。转载请参见文章末尾处的要求。 写在前面 现在,Android应用程序中集成第三方API已十分流行。应用程序都有自己的网络操作和缓...

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

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

jiangwei0910410003
04/25
0
0
Retrofit源码分析之OKHttpCall

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

低情商的大仙
05/13
0
0
Android Retrifot2简单使用学习

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

reggie1996
05/27
0
0
feign和okhttp的结合

背景 使用feign可以很方便的调用各种http接口 http请求神器之Feign 那么feign是如何做到的呢? 分析 本质上默认场景feign仍然是使用httpClient进行调用的。 通过声明式的RequestMapping等注解...

Mr_Qi
07/11
0
0
spring传统xml配置okhttp3

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

亚林瓜子
04/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

OSChina 周五乱弹 —— 我们是食物链的最底层

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @温家成 :分享谢安琪的单曲《姿色份子》 《姿色份子》- 谢安琪 手机党少年们想听歌,请使劲儿戳(这里) @贪吃飒:最近p2p怎么了、半个月爆了...

小小编辑
14分钟前
3
1
Android Studio 3.0 之后打包apk出现应用未安装问题

1、废话 出现这个问题的原因,并不是只有一个,而是有多个原因,不懂的估计会被搞得一头雾水,下面我列举的是我遇到的几种问题和网友遇到的几种问题,但不一定是全部,也有可能有些莫名其妙的...

她叫我小渝
34分钟前
0
0
前端基础

1. get请求传参长度的误区 误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。 实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是...

wenxingjun
今天
0
0
拦截SQLSERVER的SSL加密通道替换传输过程中的用户名密码实现运维审计(一)

工作准备 •一台SQLSERVER 2005/SQLSERVER 2008服务 •SQLSERVER jdbc驱动程序 •Java开发环境eclipse + jdk1.8 •java反编译工具JD-Core 反编译JDBC分析SQLSERVER客户端与服务器通信原理 SQ...

紅顏為君笑
今天
8
0
jQuery零基础入门——(六)修改DOM结构

《jQuery零基础入门》系列博文是在廖雪峰老师的博文基础上,可能补充了个人的理解和日常遇到的点,用我的理解表述出来,主干出处来自廖雪峰老师的技术分享。 在《零基础入门JavaScript》的时...

JandenMa
今天
0
0
linux mint 1.9 qq 安装

转: https://www.jianshu.com/p/cdc3d03c144d 1. 下载 qq 轻聊版,可在百度搜索后下载 QQ7.9Light.exe 2. 去wine的官网(https://wiki.winehq.org/Ubuntu) 安装 wine . 提醒网页可以切换成中...

Canaan_
今天
0
0
PHP后台运行命令并管理运行程序

php后台运行命令并管理后台运行程序 class ProcessModel{ private $pid; private $command; private $resultToFile = ''; public function __construct($cl=false){......

colin_86
今天
1
0
数据结构与算法4

在此程序中,HighArray类中的find()方法用数据项的值作为参数传递,它的返回值决定是否找到此数据项。 insert()方法向数组下一个空位置放置一个新的数据项。一个名为nElems的字段跟踪记录着...

沉迷于编程的小菜菜
今天
1
1
fiddler安装和基本使用以及代理设置

项目需求 由于开发过程中客户端和服务器数据交互非常频繁,有时候服务端需要知道客户端调用接口传了哪些参数过来,这个时候就需要一个工具可以监听这些接口请求参数,已经接口的响应的数据,这种...

银装素裹
今天
0
0
Python分析《我不是药神》豆瓣评论

读取 Mongo 中的短评数据,进行中文分词 对分词结果取 Top50 生成词云 生成词云效果 看来网上关于 我不是药神 vs 达拉斯 的争论很热啊。关于词频统计就这些,代码中也会完成一些其它的分析任...

猫咪编程
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部