OkHttp小结

OkHttp小结

OkHttp基本实现原理

  • OkHttp使用时会先创建OkHttpClient,然后创建一个Request封装网络请求参数,

  • 接着通过Client构建一个RealCall对象,发起请求有两种方式:

    • 一种调用execute方法同步发起请求

    • 另一种是调用enqueue方法发起异步请求:

      • 异步请求的时候会先把请求放到分发器Dispatcher的等待队列中,然后从队列中一个个取出请求放到线程池中并发执行

      • 最多支持同时访问5个服务器,以及最多同时发起64个请求(同步发起的请求没有这个限制)

      • 默认的线程池是一个核心线程数为0,最大线程数是整数的最大值,线程停留时间是60秒,队列是SynchronousQueue容量为0的同步队列,这意味着来一个请求就会立刻在线程中执行

  • 不管是同步请求,还是异步请求,最终都会调用getResponseWithInterceptorChain方法,在这个方法中会收集所有的拦截器,包括OkHttp自带的5个拦截器以及用户自定义的拦截器,然后将所有拦截器组装成一个拦截器链(RealInterceptorChain),然后一个个去执行拦截器,在执行一个拦截器时会将下一个拦截器封装成新的拦截器链传入,拦截器中可以选择继续执行下一个拦截器链或者直接返回结果

OkHttp拦截器介绍

  1. 用户自定义拦截器:用于普通网络请求,比如用于打印Log的拦截器

  2. 重试和重定向拦截器RetryAndFollowUpInterceptor:用于请求失败或者重定向时重新发起请求,最大重新发起请求次数是20

  3. 桥接拦截器BridegeInterceptor:处理请求头和响应头信息的封装

  4. 缓存拦截器CacheInterceptor:用于处理请求结果缓存,判断是否使用缓存以及更新缓存等;缓存策略主要分为两种:

    1. 一种存在客户端的缓存,只要缓存没过期则复用;好处是不需要经过服务器,相应快,但是如果服务端数据有更新时则客户端无法及时获取

    2. 另一种是每次都请求服务器,服务器如果判定数据没有更新则返回304并复用旧数据,如果有更新则返回最新数据;优点是可以及时获取到最新数据,同时复用缓存数据;缺点就是每次需要请求服务器

  5. 连接拦截器ConnectInterceptor:用于从连接池中查找可用连接,没有则创建

    1. 从连接池中查找可用的Socket连接,如果不存在则创建新的Socket连接并加入缓存中
    2. 通过socket和okio模拟http发送请求
  6. 用户自定义拦截器:网络拦截器NetworkInterceptor:仅用于Socket连接时使用的拦截器

  • 请求服务器拦截器CallServerInterceptor:用于真正进行网络请求,通过OKIO发送请求和获取结果,获取结果后对原始数据进行解析和封装

OKIo

okio最早是出现在okhttp中用于处理socket、缓存中的io流,由于它使用非常简单高效、也可以单独拿出来用于替换java原生io处理;okio具有以下特点:

  • 对java的 IO/NIO的二次封装,简化了很多嵌套,支持Socket的读写

  • 不区分字节流、字符串流;统一成了输入流Source和输出流Sink

  • 新增大量api简化操作,比如支持同时写入字符串流、字节流、方便读取一行或者全部文本、支持计算md5、hash值等

  • 读写速度非常快是因为有一套自己的Buffer机制,在Buffer中通过一个个segment以双向链表形式保存缓存数据,每个segment中最多保存8k数据,SegmentPool缓存池中保存着可以复用的Segment,当segment不再使用的时候又会进入缓存池,缓存池最多缓存8个Segment,segment的复用机制减少了内存开销,在拷贝文件时减少了拷贝次数,从而提高效率

  • io超时机制,分为同步超时和异步超时

    • 同步超时Timeout:在读写io之前判断是否已经超时,超时就抛出异常,缺点就是当线程被阻塞时,只有等待下一次读写io时才能抛出异常

    • 异步超时AsyncTimeout:在进行读写IO之前会将异步超时对象按照超时先后顺序保存到链表中,并开启一个线程不断的去判断超时对象是否超时,如果超时则调用超时的回调函数并移除队列;默认Socket就是采用这种异步超时机制

    • 除了socket,普通io不支持异步超时机制,导致超时时不能及时抛出异常,只能等待下一次io才能发现

io/nio区别:

  • io是面向流的,而nio是面向缓冲区的

  • io需要频繁进行内核拷贝,而nio是在缓冲区积攒到一定量才进行操作,减少了内核拷贝次数

  • io每次读取完数据没有任何缓冲的地方,无法在数据流中移动;而nio是先读取到缓冲区,然后再对缓冲区的数据进行操作,操作起来更加灵活

  • io是阻塞的,当在进行读写时,当前线程会被阻塞;而nio支持非阻塞式io;当读写时不会一直阻塞线程;

  • nio支持一个线程管理多个通道进行读写

  • io适合频次少,每次发送数据多的场景;而nio适合频次高,但是每次发送数量不多的场景

小结:

  • 你为什么使用OkHttp?

    答:okhttp网络请求效率非常高,内部通过各种拦截器,帮我们处理了重试、重定向、缓存等问题,通过socket连接复用、模拟http请求、使用okio发送和接收网络数据;可扩展性也非常高。

  • 完成一次Http请求的过程是怎样的?

    答:先创建一个OkhttpClient设置一些基本配置信息,比如缓存策略等等,然后创建一个Request请求进行一些请求方面配置,比如url、头部信息、请求的参数等等;然后通过Client和Request构建一个Call对象,Call的实现类是RealCall,接着调用它的execute或者enqueue方法进行同步或者异步网络请求;如果是异步请求,会先把请求保存到Dispatcher分发器中的队列中,然后从队列中取出网络请求任务放到线程池中运行,不管是同步还是异步请求,最后都会调用到RealCall的getResponseWithIntercepterChain方法;在这个方法里会将用户自定义拦截器、自带的五种拦截器组装成一个拦截链;接着就是一个个的去调用各个拦截器的方法进行处理,比如重试/重定向拦截器、桥接拦截器、缓存拦截器、连接拦截器、请求服务拦截器等;如果本地没有缓存则请求网络并返回结果,网络请求是通过scoket模拟http请求实现以及通过okio进行网络数据的发送和接收以及缓存的处理

  • 你觉得OkHttp还能够优化一下?

    答:okhttp里用到的okio还可以优化一下超时机制,默认的okio是在调用sink或者source方法时自动设置TimeOut的,普通的IO是走的同步超时机制,Socket走的是异步超时机制,但是我们作为开发者没办法传入自定义的TimeOut,而同步超时又有个缺点就是每次io超时时不能及时抛出异常,只能等待下一次io时才能发现;我们想使用异步TimeOut却没有方法可以传入;所以可以改进下让它支持用户传入自定义的TimeOut增加扩展性;

  • 什么是动态代理?和静态代理的区别是什么?JDK中动态代理是怎么实现的?

    答:静态代理是指我们编码时编写的代理类,有个缺点就是对于需要大量且功能统一时的代理类时,就会产生大量的代理类,一旦改动某个接口大量的代理类也要跟着改,而动态代理是在运行时动态生成的代理类,通过调用java自带的Proxy.newProxyInstance方法,传入要ClassLoader、要代理的接口、以及InvocationHandler就可以动态创建一个代理类的实例,内部是通过native方法动态生成的代理类并实例化对象,生成的代理类继承自Proxy类,同时实现我们传入的接口,在调用代理类方法时,会调用我们传入的InvocationHandler的invoke方法进行业务处理

  • Okhttp中用到哪些设计模式?解决了什么问题?

    答:在构建OkHttpClient、Request对象时使用了建造者模式,它使得构建对象可以按照需要随意定制,不需要设置的就使用默认值;

    在网络请求过程中使用了责任链模式,通过将用户自定义和自带的拦截器连接成链式结构,每一个拦截器都可以对请求数据和返回结果进行处理和拦截,使得网络请求各个阶段的职责比较明确,也易于扩展功能;

    在Socket连接池中使用了享元模式,将使用完后的Socket连接缓存起来,供下一次发起网络请求时请求,避免了多次建立连接时的开销

  • 怎么使用OkHttp或者Retrofit完成文件下载上传?

    答:下载时在onResponse回调方法中,获取到ResponseBody对象,拿到BufferedSource数据流,然后通过OKio创建BufferedSink对象写入BufferedSource数据流保存到文件就可以了;上传时需要通过RequestBody将需要上传的文件作为参数使用post方式发起请求

  • 如果网络比较差,2G/3G,怎么进行弱网优化?

    答:建立重试机制、缓存机制、合并多个网络请求、减少网络请求传送的数据、弱网时使用UDP协议,UI上提示用户网络较慢耐心等待或者切换网络

  • Http1和Http2的区别是什么?

    答:http1只能支持纯文本数据发送,而http2采用的是二进制数据;http2支持多路复用,也就是一个连接上支持多个请求;http2还支持服务端主动发送消息给客户端

  • 谈谈TCP与UDP的理解

    答:TCP是可靠的传输协议,在发送和接收数据之前,需要经过三次握手建立连接,断开连接时需要经过四次挥手,当数据发送失败时会有重发机制来保证数据传输的可靠性;而UDP发送数据前则不需要建立连接,直接像广播一样发送出去就完事了,不需要管对方有没有接收到,无法保证数据传输的可靠性,但是它相比TCP要快很多,适合直播,网络通话等对数据可靠性要求没那么高的场景

  • okhttp为什么会使用okio而不是用普通io

    答:因为java自带的io使用起来比较繁琐,效率不高,okio是在原生java io的基础上做了二次封装,新增了很多新的api简化操作,只有两个数据流,一个是Source用于读取数据,一个是Sink用于写入数据,不像原生io那样需要套用多个IO流才能完成某个功能;在性能上,okio有自己的Buffer缓存,它的Buffer是由一个或者多个Segment片段组成,每个Segment可以存储8k数据,通过链表的形式连接起来;而Segment则由Segment缓存池SegmentPool进行维护,通过缓存池来回收不再使用的Segment,从而避免频繁的开辟内存空间;Segment缓存池最多同时缓存8个Segment;通过这种缓冲设计,可以降低文件拷贝时数据流的拷贝次数,降低内存空间的使用,从而提高IO效率

  • okhttp中socket连接池怎么复用的

    答:socket连接之后,会缓存起来,而不会立刻断开连接,当下次有网络请求时直接找到已有的符合条件的Socket连接发送请求,没有时才会创建新的Socket连接,socket连接池的设计使得节省了tcp连接的时间,加快了网络请求的速度和响应时间;采用了类似于GC的标记清除算法,通过一定的算法比如最少时候或者超时等来判断是否需要标记清除

  • Https加密过程

    • HTTPS加密过程:

      • 客户端发起https请求从服务端获取证书和公钥
      • 客户端校验证书的真实性
      • 客户端使用服务端的公钥加密生成的随机数作为通信时的密钥
      • 服务端使用私钥解密密钥后后获取随机数
      • 客户端和服务端接着就可以使用随机数和密钥对传输过程的数据进行加密和解密
    • 附:

      • 非对称加密:比如RSA算法;采用公钥和私钥分别管理,公钥用于加密数据,私钥用于解密数据;安全性非常高,但是加密解密过程慢

      • 对称加密:比如DES\AES等;加密和解密都公用一个密钥,安全性相对较低,但是加密解密速度很快

      • https加密过程先使用非对称加密算法对随机加密生成一个用于对称加密的密钥,然后用随机生成的对称加密密钥对传输过程中的数据进行加密解密,从而既用到了非对称加密的安全性也兼顾到了对称加密的高效性

      • HTTPS默认使用443端口,而HTTP默认使用80端口。

      • TLS就是从SSL发展而来的,只是SSL发展到3.0版本后改成了TLS;它们是在tcp/ip之上的网络安全协议

      • 第一次请求中TLS握手的代价很大,后续的请求会共用第一次请求的协商结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值