【Android】OKHttp源码简介

OkHttpClient是所有OkHttp请求的客户端类,创建一次,作为全局实例来保存,其他地方只会使用这一个单例对象。
然后创建Request对象,封装了一些请求报文信息,url地址,请求方法get、post等,通过Builder模式,用链式方法生成Request对象。
然后通过client的newCall方法得到一个RealCall对象(Call是一个接口,它的具体实现是在RealCall中),RealCall代表一个实际的Http请求,它是连接Request和Response的桥梁(client.newCall(request)获得Call对象,即将Request封装为Call对象),call.execute()同步请求,得到Response对象(代表响应报文信息,状态码响应头等等)

之后是Dispatcher,决定请求是异步还是同步,里面维护了一个线程池,用于支持网络请求,RealCall会把请求添加到Dispatcher单元类中。Dispatcher中有三个队列来维护同步或异步请求
interceptors:拦截器,用于服务器数据的获取。构建一个拦截器链,然后依次执行里面的每个拦截器,将服务器获得的数据返回

5个拦截器:

(1)RetryAndFollow:重试,或重定向
(2)Bridge:设置请求内容长度,编码,gzip压缩,同时可以添加cookie
(3)Cache:负责缓存管理,如果cache里面有直接返回,而不需要再经过网络端
(4)Connect:为当前请求找到一个合适的连接,因为有时候会复用已有的连接,如果连接可以复用,就不必再重新创建了
(5)CallServer:向服务器发起真正的网络请求,并返回

两个重要的模块:Dispatcher和Interceptors,Dispatcher会不断的从Request队列中获取RealCall请求,然后根据是否调用缓存,用缓存拦截器来拦截,如果没有缓存,则调用Connect和CallServer来获取服务器数据返回。Dispatcher来决定是调用同步接口还是异步接口,不管同步还是异步,最后都会通过拦截器来处理

创建OkHttpClient有两种方式,一个是new OkHttpCllient,但是没法设置参数,第二种是使用它里面的静态Builder类,用builder模式来塞入自己要定义的参数

OkHttp同步请求方法总结:

1、创建OkHttpClient和Request对象,两者都使用了Builder模式
2、将Request封装成Call对象
3、调用Call的execute()发送同步请求

同步请求发送后,就会进入阻塞状态,直到收到响应(异步不会阻塞当前线程,异步中会开启一个工作线程,去进行数据的处理)

OkHttp异步请求方法总结:

1、创建OkHttpClient和Request对象,两者都使用了Builder模式
2、将Request封装成Call对象
3、调用Call的enqueue进行异步请求

用call.enqueue发起异步请求,里面会开启一个新的异步线程的,成功后会回调CallBack中的onResponse方法,如果请求失败或者调用cancel方法,会回调onFailure方法,
注意:onResponse和onFailure都是在子线程中执行的。

OkHttpClient的Builder中的ConnectionPool 连接池,将服务器和客户端每个连接放到连接池中,由它来做统一管理。当请求的url是相同的,可以选择复用。

Builder对象,最后会将参数传递到构造方法中,完成对象属性的初始化。
如果创建的对象需要很多参数,则可以选择只用Builder这种创建模式

RealCall中持有了OkHttpClient和Request对象,同时赋值了一个RetryAndFollow拦截器
同步请求:Dispatcher负责保存队列,然后在结束的时候移除队列。

readyAsyncCalls 缓存等待的请求队列
runningAsyncCalls 正在运行的任务,用于判断并发请求的数量。包含已经取消了,但是还没执行完的请求

异步请求:
Enqueue方法总结

1、判断当前Call是否执行一次,如果否,则会抛出异常
2、封装成一个AsyncCall对象(通过传递的Callback对象,结合Call构建成一个AsyncCall对象,它实际上是一个Runnable)
3、client.dispatcher().enqueue()

发起异步网络请求时,会先判断两条件,当前运行的runnable个数是否小于64,且同一个主机发送的请求数小于5,如果条件都满足,则将它添加进去,然后用线程池执行该请求,否则添加到就绪队列中

Dispatcher中的executorService被synchronized锁住,是为了保证线程池是一个单例对象,虽然该线程池中设定为Integer.Max,但是不会无限创建线程,因为在dispatch前判断要小于maxRequests,才添加到线程中

executorService.execute(call)里面会调用call的run方法–>NameRunnable,它里面的run最终调用了execute,该方法在AsyncCall中被实现。

在finally中,会调用finished方法,执行一些销毁操作。里面用synchronized,因为里面的promoteCalls调整任务队列,该队列是非线程安全的。然后重新计算正在运行队列中的数量,重新赋值,因为后续会用这个变量做判断。

Dispatcher
发送的同步和异步请求都会在Dispatcher中管理状态
Dispatcher用于维护请求的状态,并维护一个线程池(在dispather内部类中ExecutorService),用于执行请求。

异步请求为什么需要两个对列:
可以想象成生产者-消费者模型
Dispatcher 生产者,它是在主线程的
ExecutorService 消费者池
因此需要两个队列,一个放正在执行的异步请求,一个放等待的异步请求。

同步的Dispatcher中的execute,只是将call添加到runningSyncCalls中,没有做其他的处理

在ExecutorService中ThreadPoolExecutor第一个参数,核心线程数为0,即如果空闲一段时间,会将所有的线程全部销毁,第二个参数设置为整型的最大值,尽管这里没加限制,但是因为前面Dispatcher做了限制,所以不会无限的创建线程的。第三个是当线程数大于核心线程数时,多余的空闲线程存活的时间,这里设定为60s

Call执行完肯定需要在runningAsyncCalls队列中移除这个线程,因为如果不删除,后面的等待缓存中的call没法添加进来。在AsyncCall里面的execute里面调用Dispatcher的finished方法,该方法中调用remove方法,将原来的Call移除,然后调用promoteCalls方法来调整任务队列,因为队列是非线程安全的,所以该方法外部有一个synchronized同步。然后调用runningCallCount方法,即重新计算线程数量。

promoteCalls:前面两个判断,正在执行的任务小于最大请求,且等待队列中有任务才能进入下面的代码。首先遍历readyAsyncCalls队列,然后找到最后一个元素,从中移除,然后添加到runningAsyncCalls当中。

拦截器:
拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。

拦截器有两种:Application拦截器和Network拦截器。
OkHttp Core中是系统提供的一些内部拦截器

拦截器不区分同步还是异步,即它们都会使用拦截器,来进行实际的网络相应的获取。

OkHttp会通过一个拦截器链来执行http请求

RetryAndFollowUpInterceptor:重试和重定向拦截器,主要做一些初始化工作,会创建StreamAllocation对象,用来传递给后面的拦截器,

BridgeInterceptor:桥接和适配拦截器,补充创建的请求中,缺少的一些Http请求头,

CacheInterceptor:处理缓存

ConnectInterceptor:负责建立可用的连接,它是CallServerInterceptor的基础

CallServerInterceptor:负责将Http请求写入到网络IO流当中,并从网络IO流中读取从服务器返回给客户端的数据

getResponseWithInterceptorChain:构建出一个拦截器的链,然后依次执行每个不同功能的拦截器,来获取服务器的响应返回。
该方法中,首先添加了client.interceptors()是用户自定义的拦截器,即应用程序拦截器。然后添加一些系统内部的拦截器,最后判断forWebSocket,然后添加client.networkInterceptors()是网络拦截器。

创建RealInterceptorChain,然后调用它的proceed方法执行请求,在RealInterceptorChain的proceed中,143行,创建一个新的拦截器链,传入参数是index+1,即如果访问,只能从下一个拦截器开始访问,而不能从当前拦截器,从而将拦截器构成了一个链条。147行,调用索引为index的inerceptor的intercept方法获得Response,参数是刚才创建的next拦截器链,从而构成一个链条调用。

拦截器总结1

1、创建一系列拦截器,并将其放入一个拦截器list中,里面包含应用程序拦截器和网络拦截器。
2、创建一个拦截器RealInterceptorChain,并执行拦截器链的proceed方法(该方法的核心是创建下一个拦截器链,从而依次调用下一个拦截器的intercept方法)

网络请求当成 一个个拦截器执行它的intercept方法的过程,每个intercept中的proceed方法会执行下一个拦截器的intercept方法

拦截器总结2:intercept方法执行逻辑

1、发起请求前对request进行处理
2、调用下一个拦截器,获取Response(要调用下一个,因为有拦截器链的概念,调用下一个,从而构成一个链条)
3、对response进行处理,返回给上一个拦截器。

RetryAndFollowUpInterceptor
负责失败重连,但不是所有的http请求都可以重连的,里面会有一些判断条件
它首先创建了StreamAllocation,它是http请求所需要的网络组件。虽然在这创建,但是没有在这使用,到ConnectInterceptor中被使用到。

StreamAllocation两个作用:

(1)获取服务端的连接Connection
(2)用于服务端传输的输入输出流

StreamAllocation通过拦截器链最终传递到ConnectInterceptor中使用的

拦截器链执行过程可以当做递归的执行过程,内部通过一个RealInterceptorChain负责所有的拦截器,并把他们串联起来,所以,只有当所有的拦截器执行完之后,这个网络请求才会返回Response

网络环境复杂,在请求的任何过程中都有可能出现失败,最终包裹在Response里面的错误码
网络请求失败,会去重试,但是不会一直去重试,毕竟移动端资源也有限

RetryAndFollowUpInterceptor

(1)创建StreamAllocation对象
(2)调用RealInterceptorChain.proceed进行网络请求
(3)根据异常结果或者响应结果判断是否要进行重新请求
(4)调用下一个拦截器,对Response进行处理,返回给上一个拦截器

BridgeInterceptor:对Request添加头部信息,让它成为发送请求的Request
设置内容长度,编码方式,压缩等,主要是添加头部的作用
keep_alive实现连接复用的基础,keep_alive指开启一个TCP连接后,不会立即关闭连接,在该段时间内,保持连接状态。
HttpHeaders.receiveHeaders:将服务器返回的response,转换为用户可以使用的Response
如果服务器返回的是gzip压缩的,则BridgeInterceptor要负责去解压
客户端需要告知服务器,支持gzip压缩,服务器才会返回gzip的响应

BridgeInterceptor

(1)是负责将用户构建的一个Request请求转化为能够进行网络访问的请求
(2)将这个符合网络请求的Request进行网络请求
(3)将网络请求回来的响应Response转化为用户可用的Response(比如gzip解压等等)

okhttp的缓存策略
使用:在创建OkHttpClient时,用cache(new Cache(new File(“cache”), 2410241024)就可以了,创建的Cache类有两个参数,一个是File对象,它是缓存的目录,第二个是缓存的大小

Cache类中的InternalCache内部类,它里面的方法都是Cache来实现的,找到put方法,里面有DiskLruCache.Editor,即最终是通过DiskLruCache算法来实现的,Okhttp内部维护一个清理的线程池,由它来实现缓存文件的自动清理和管理。
put方法中不会去缓存非Get请求,Entry是要缓存的对象
key方法:将Url做md5加密,然后hex得到十六进制
在Entry的writeTo方法只是对请求的头部和响应的头部信息进行存储,而响应中的body在后面返回的是new CacheRequestImpl,它里面有响应的body,它继承自CacheRequest,为了后面的CacheInterceptor来使用

get方法:
从缓存中读取响应Response
DiskLruCache.Snapshot:缓存快照,记录缓存在某个特定时刻,包含的内容。即从DiskLruCache请求时,得到的是缓存中的一个快照。
用snapShot来得到Entry,然后从Entry中得到Response,在entry.response()中
用snapshot来new CacheResponseBody,它里面就对snapShot进行读取获得Response

CacheInterceptor:intercept方法中,首先调用cache.get方法,即会尝试首先获取缓存,
CacheStrategy:是使用网络还是使用缓存得到Response,也可以同时使用两者进行对比来决定哪种策略。
CacheStrategry.Factory.getCandidate()方法来得到具体的策略
cache.trackResponse(strategy)里面是对命中数加1操作,提高下一次的命中率

ConnectInterceptor:打开与服务器之间的连接,正式开启与Http的网络请求,每个拦截器都覆写了intercept方法,然后里面最终又调用chain.proceed方法传递给下一个拦截器
realChain.streamAllocation()得到StreamAllocation,它是在RetryAndFollowInterceptor中初始化的,在这个拦截器才使用
然后调用streamAllocation.newStream得到HttpCodec对象,它用于对请求进行编码,或者对服务端返回结果进行解码。
RealConnection来真正进行网络IO传输的

ConnectInterceptor

(1)ConnectInterceptor从拦截器链中获取前面的Interceptor中传过来的StreamAllocation,并调用streamAllocation.newStream初始化一个HttpCodec对象
(2)将刚才创建的用于网络IO的RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截器

StreamAllocation(分配Stream)里面findConnection方法,先看能否复用,不能复用再去从连接池connectionPool中获取一个连接
获取Connection之后,调用它的connect方法来进行实际的网络连接,然后把它放到连接池中(Internal.instance.put方法)

总结:

(1)获取一个RealConnection对象
(2)选择不同的连接方式(选择是隧道还是Socket)
(3)最后调用CallServerInterceptor,来完成最终的网络请求

OkHttp中两种连接:(1)隧道连接(2)Socket连接

RealConnectionConnection的实现类,为了管理所有的ConnectionOkHttp提供ConnectionPool,在一定时间内,复用连接。并提供连接回收的功能。

StreamAllocation.acquire:首先将从连接池中获取的连接赋值给StreamAllocation对象的connection,然后将StreamAllocation的弱引用添加到realConnection的allocations集合中,该集合用于判断当前连接对象所持有的StreamAllocation数目,通过集合的大小来判断一个网络连接的负载量是否已经超过了它的最大值。

put方法:首先做了异步清理任务(cleanupRunnable一个异步清理任务,让线程池去执行,里面有回收connection的操作),然后把realConnection连接到connections队列中

Connection的回收:利用GC回收算法,当StreamAllocation的连接数量渐渐变为0,当为0时,会被线程池检测到,然后回收,这样就可以保持多个健康的keep alive连接。
cleanUpRunnable来清理连接池,它是一个死循环(即会阻塞在这里),首次进行清理时,要返回下一次需要清理的间隔时间waitNanos,ConnectionPool.this.wait等待释放锁和时间片,当等待时间过了之后,它会再次调用Runnable去进行清理,同时返回下一次清理的间隔时间。
cleanup即真正的GC回收算法,类似于GC中的标记清除算法,首先标记哪些最不活跃的Connection连接(或称之为泄露连接,或空闲连接idle),
pruneAndGetAllocationCount:找到最不活跃的连接,根据弱引用是否为null来判断,

CallServerInterceptor:负责向服务器发起真正的网络请求,然后接收服务器返回的响应

HttpCodec:编码Request,解码Response
httpCodec.writeRequestHeadersSocket中写入请求的头部信息
CallServerInterceptor:上方执行的是Request的写入,下面是执行Response的读取操作

网络请求:head和body信息

OkHttp中一次网络请求的大致过程:

(1)将请求封装为Call对象
(2)Dispatcher对请求Request分发
(3)getResponseWitehInterceptors方法(里面是拦截器链)

Android基础网络编程:
Socket:
Socket是基于应用层和传输层之间抽象出来的一层,可以通过它来发送和接收数据,即它提供了应用程序与外部通信的一个端口,为两端通信提供了数据传输通道。(抽象出来的)

socket是什么:

(1)它是对TCP/IP协议进行封装的编程调用接口(socket不是协议,只是编程调用的API,属于传输层)
(2)成对出现,一对套接字:包括ip地址和端口号

传输层有TCP或UDP两种,所以socket通信也分为两种,
TCP是采用字节流的方式提供可靠的传输,
UDP采用数据报文的形式,打包发送服务。

Android与服务器通信的方式:

(1)Http通信,基于请求和响应方式(有请求,才有响应,客户端有需要才进行通信),属于应用层协议,解决的是如何包装数据
(2)socket通信,它是基于TCP/IP协议的,所以,如果双方建立了连接,就可以直接进行传输。socket采用的是服务器主动发送数据的方式(服务端有需要才进行通信),socket属于传输层,解决的是数据如何在网络中传输的问题(传输层做的事)。

Socket也是CS架构的,
客户端:
socket= new Socket(“192.168.1.100”, 8888),两个参数,目标主机ip和端口号,也可以传主机名,它会进行DNS解析。
然后output = socket.getOutputStream()获取输出流,然后可以对它写入信息,output.write(msg.getBytes())写入字节流,写完后,调用socket.shutdownOutput()方法,如果不调用该方法,有可能收不到服务端的消息返回。最后关闭socket.close(),前面的输出流因为我基于socket创建的,所以关闭了socket即可,不用再关闭输出流。如果自己创建的io流,则要手动关闭。

服务端:
ServerSocket server = new SeverSocket(8888)监听客户端端的8888端口,不用指定ip。调用server.accept来接收客户端发送的请求,它返回一个socket对象,如果客户端没有发送数据,则线程会在该方法中阻塞,然后调用socket.getInputStream得到输入流。
最后关闭的是socket,但是serverSocket不可以被关闭,如果关闭,意味着不会再监听客户端发送的数据

HttpClient:Android 6.0以后,移除了,它的使用简单

HttpURLConnection:
URLEncoder.encode来对字符进行编码
创建URL对象(全大写的URL对象),然后调用它的openConnection来得到一个HttpURLConnection,然后调用它的conncetion.connect()方法来进行连接,但此时并没有发送request数据,在之后调用connection.getInputStream()才发送request数据,然后客户端才能进行数据的读取操作,最后关闭io流,然后关闭连接connect.disconnect,因为这里的连接是持久化的,所以要自动断开,调用该方法,最终关闭底层的socket连接。

WebSocket:
推送技术:轮询(polling)
轮询是在特定的时间间隔,由浏览器对服务器发出Http请求,然后由服务器返回最新的数据给
客户端浏览器
轮询:
(1)短轮询
到时间,就发送request去请求数据
缺点:在某个时间段内,server没有更新数据,但是client仍然每隔一段时间就发送请求来询问,所以这段时间内的询问都是无效的(因为信息都是一样的)

(2)长轮询
客户端发送request后,服务端接收到请求后,不会立即返回给客户端,而是会看数据是否更新,如果更新,则返回给客户端,如果没有更新,则会把这个requset保持住,知道服务端有最新的数据时,才会返回给客户端。

轮询的缺点:

(1)浪费带宽(用的Http请求发送的,Http Head是比较大的)
(2)消耗服务器CPU占用(让服务器去处理一些没有用的请求,所以很浪费)

WebSocket:一旦客户端和服务端建立了连接,两者就可以不断的通信,相互发送数据,它是全双工的通信模式,只要连接不close掉,都可以不断发送数据。

WebSocketHttp
Http是懒惰的协议,只有客户端发送了请求,才会去响应。

W3C在HTML5中,提供了客户端和服务端全双工通信的网络技术WebSocket,它解决了server主动向client发送数据的问题,只要连接不断开,就可以不断发送数据。

WebSocket

(1)与Http同等的网络协议
(2)全双工的通信协议
(3)本质是一个基于TCP的协议
(4)为了与服务器建立WebSocket连接,要首先向服务器发送一个Http请求

“Upgrade:WebSocket”,该http请求不同于一般的Http请求,它加入了一个头部信息Upgrade:WebSocket,服务端接收到这个请求后,会去解析这些附加的头信息,然后产生Response给客户端,这样就建立一个WebSocket连接,建立连接后,就不需要http了。

WebSocketSocket

(1)socket本身不是一种协议,它只是为了方便使用TCP/UDP,而抽象出来的一层,是介于应用层和传输层之间的一组接口
(2)WebSocket是一种协议,与Http同等的网络协议

所以WebSocket与Socket是没有关系的

用OkHttp也可以创建WebSocket请求:
OkHttpClient client = new OkHttpClient()
Request request = new Request.Builder().url("").build();
WebSocketListener listener = new WebSocketListener ();(该接口实现在WebSocket上实现多种消息的异步通知,该listener中有很多的回调方法,使用时,一般要去继承该接口,然后去实现其中对应的异步回调方法比如onOpen, onMessage, onClosing(准备关闭), onClosed(连接被完全关闭),里面的方法是使用OkHttp后台程序来发送数据,即工作在子线程中,所以不会担心阻塞,但是同时因为在子线程中,如果想与UI线程交互,则要使用主线程的Handler来sendMessage实现)
client.newWebSocket(request, listener);
最后使用完毕后,调用client.dispatcher().executorService().shutdown()关闭Dispatcher中的线程池

Http缓存:
1、强制缓存
(1)Expires
它的值为服务端返回的到期时间,如果下一次请求时间小于该时间,直接使用缓存。在Http 1.0上用到该字段,现在浏览器默认是Http 1.1了,该字段暂时被忽略
问题:到期时间是由服务端生成的,与客户端产生是时间是有误差的,从而造成缓存读取的误差
(2)Cache-Control
它是由服务器返回的Response中添加的头信息,它是告诉客户端,是从本地读取缓存,还是从服务端读取信息,
Cache-Control:max-age=315360000
表示缓存的内容在31536000s后失效
它的值有5种
private 表示客户端可以缓存
public 表示客户端和代理服务器都可以缓存
max-age 表示缓存内容将在多少秒后失效
no-cache
no-store 所有内容都不需要缓存

2、对比缓存
首先需要进行比较判断是否可以使用缓存
在向服务器请求数据时,服务端会将数据和缓存标识返回给客户端,客户端就将数据进行缓存
Etag/if-None-Match 两者成对出现
Etag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(它的生成规则由服务器端决定)
if-None-Match:再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识

Last-Modified/If-Modified-Since
Last-Modified:服务器在响应请求时,告诉浏览器资源的最后修改时间
if-Modified-Since:再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间

断点续传:
从文件已经下载的地方开始继续下载
客户端发送给服务器的报文中,多加一条信息,表明这次下载从哪里开始
在Request头部中,添加RANGE表示,比如RANGE: bytes=200080- 从这个字节处开始下载

url = new URL("http://www.xxx.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
httpConnection.setRequestProperty("RANGE", "bytes=200080")

InputStream input = httpConnection.getInputStream();
RandomAccessFile oSavedFile = new RandomAccessFile("down.zip", "rw");
long nPos = 2000070;
oSavedFile.seek(nPos);
byte[] b = new byte[1024];
int nRead;
while((nRead=input.read(b, 0,1024))>0)
{
oSavedFile.write(b, 0, nRead);
}

OkHttp的方式

Request request = new Request.Builder()
.addHeader("RANGE", "bytes="+downloadLength+"-)
.url(downloadUrl)
.build();

然后得到的Response,response.body().byteStream()得到字节流,不断读取,然后写入到RandomAccessFile中

多线程下载:
每个线程只负责下载文件的一部分

文件上传:
ContentType:指定请求和响应的Http内容类型
在OkHttp中,创建MultipartBody

json:
文本形式的数据交换格式,比xml更轻量,编写容易

json解析

(1)传统的json解析
(2)GSON,调用它的fromJson方法,得到对象,调用toJson方法得到json字符串
(3)FastJSON

Https:是一种基于ssl/tls的http协议,在http协议上添加了ssl/tls握手过程,以及数据加密传输,仍然是应用层协议。

http缺点:所有传输的内容都是明文
https:所有传输的内容都经过加密的(对称+不对称)

对称和不对称,表明的是加密和解密是否使用同一个密钥

对称加密速度快,所以在数据传输时,仍然使用对称加密

服务器将不对称的公钥(证书中即公钥)传递给客户端,客户端将对称密钥用不对称的公钥来加密,然后传递给服务器,服务器用自己的不对称私钥来解密客户端的对称密钥,之后,客户端和服务器之间的通信,使用对称密钥即可,因为速度快。
真正传输数据时,用的是对称加密算法,而不对称密钥仅用在握手阶段,是为了把对称密钥安全的传递给服务器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OkHttp是一个开的轻量级框架,由Square公司贡献,用于在Android端进行网络请求。它提供了几乎和java.net.HttpURLConnection一样的API,因此使用OkHttp无需重写您程序中的网络代码。OkHttp可以替代HttpUrlConnection和Apache HttpClient,特别是在Android API23之后,HttpClient已经被移除。\[1\]\[2\] OkHttp是一个相对成熟的解决方案,在Android API19 4.4的码中可以看到HttpURLConnection已经被替换成了OkHttp。此外,OkHttp还支持HTTP/2协议,允许连接到同一个主机地址的所有请求共享Socket,从而提高请求效率。\[3\] #### 引用[.reference_title] - *1* [AndroidOkHttp的理解和使用](https://blog.csdn.net/JMW1407/article/details/117898465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android之网络请求2————OkHttp的基本使用](https://blog.csdn.net/weixin_50961942/article/details/127738248)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android OkHttp](https://blog.csdn.net/weixin_44826221/article/details/109801566)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值