文档章节

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

k
 kim366
发布于 2016/05/13 19:08
字数 1250
阅读 8
收藏 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。

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

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
私信 提问
Android技能树 — 网络小结之 OkHttp超超超超超超超详细解析

前言: 本文也做了一次标题党,哈哈,其实写的还是很水,各位原谅我O(∩_∩)O。 介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,...

青蛙要fly
11/13
0
0
手写Android网络访问框架OkHttp(简易版)

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

进击的欧阳
05/06
0
0
Android Okhttp缓存:精细化每一个Request的CacheControl缓存控制策略(二)

Android Okhttp缓存:精细化每一个Request的CacheControl缓存控制策略(二) 之前我写的附录文章1,只是简单的使用缺省的方法实现Okhttp的缓存。现在使用CacheControl,精细化到每一个Reque...

开开心心过
2017/10/24
0
0
OkHttp 3.x 源码解析之Interceptor 拦截器

Tamic / 开发者技术前线 OkHttp拦截器原理解析 在进行下文前,先说明一点,本文面向的是对Okhttp有一定基础的读者,Okhttp基础使用请阅读我的其他OKhttp+Retrofit+RxJava基础用法的文章: OK...

10/28
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

没有更多内容

加载失败,请刷新页面

加载更多

ArrayList的实现原理以及实现线程安全

一、ArrayList概述 ArrayList是基于数组实现的,是一个动态的数字,可以自动扩容。 ArrayList不是线程安全的,效率比较高,只能用于单线程的环境中,在多线程环境中可以使用Collections.syn...

一看就喷亏的小猿
21分钟前
0
0
Netty 备录 (一)

入职新公司不久,修修补补1个月的bug,来了点实战性的技术---基于netty即时通信 还好之前对socket有所使用及了解,入手netty应该不是很难吧,好吧,的确有点难,刚看这玩意的时候,可能都不知道哪里...

_大侠__
昨天
4
0
Django简单介绍和用户访问流程

Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。 Django是一个开放源代码的Web应用框架,由Python写成。 Django遵守BSD版权,初...

枫叶云
昨天
8
0
EOS错误代码及中文释义

本文集汇总了EOS区块链常见错误代码及其含义,完整错误代码集请查看 EOS错误代码集 - 汇智网 EOS错误代码列表如下, <table class="table table-striped"> <thead> <tr><th>错误代码</th><t......

汇智网教程
昨天
5
0
Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)

应用场景 之前我们已经通过《Spring Cloud Stream消费失败后的处理策略(一):自动重试》一文介绍了Spring Cloud Stream默认的消息重试功能。本文将介绍RabbitMQ的binder提供的另外一种重试...

程序猿DD
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部