okhttp之复习整理

一okhttp 

1.简介:

官方简介:OkHttp是一个默认高效的HTTP客户端

1、HTTP2支持允许对同一主机的所有请求共享一个套接字。

2、透明GZIP缩小了下载大小。OKHttp优点--使用GZip压缩减少传输的数据量, 缓存(减少重复请求);

3、连接池减少了请求延迟。

4、响应缓存完全避免了网络重复请求。

5、请求失败自动重试主机的其他IP,自动重定向:当网络出现问题时,OkHttp的不断重试,它会默默地从常见的连接问题中恢复,如果您的服务有多个IP地址,如果第一次连接失败,OkHttp将尝试备用地址,这对于IPv4+IPv6和冗余数据中心中托管的服务是必要的,OkHttp支持现代TLS功能(TLS1.3、ALPN、证书固定),它可以配置为回退以实现广泛的连接。

6、好用的API:使用OkHttp很容易,它的请求响应API设计有流畅的构建器和不变性,它支持同步阻塞调用和带有回调的异步调用。

总结:
 OkHttp底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的)。
但是OkHttp实现连接池概念,即对于同一主机多个请求,公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现连接池的概念;
 而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。
 

OkHttp相关?

OKhttp是对http协议的封装, 比较底层,因此拓展性强,便于封装;OKhttp基于NIO(JDK1.5,非阻塞式IO)效率更高。
OkHttp支持同步和异步数据请求,但异步请求是在子线程 (因为原生OkHttp的使用时回调方法是在子线程进行的,要刷新界面还需要用Handler作处理,可以使用第三方的okhttp-utils,Okgo等等);
OkHttp里面封装了线程池、数据转换、GZIP压缩(减少流量的传输)、HTTP协议缓存等,
失败重试(如果你的服务有多个IP地址,如果第一次连接失败,OKHttp将使用备用地址)

okhttp原理?
okhttp主要实现异步、同步的网络操作,创建了不同的call对象,call对象是一个个的runnable对象,由于任务是很多的,因此这里有Dispatcher包装了线程池来处理不同的call。其中该类中创建三种队列,分别用于存放正在执行的异步任务,同步队列,以及准备的队列。
最后在执行每个任务的时候,采用队列的先进先出原则,处理每一个任务,都是交给了后面的各种拦截器来处理,有请求准备的拦截器、缓存拦截器、网络连接的拦截器,每一个拦截器组成了一个责任链的形式,到最后返回response信息。

2. Okhttp请求流程图: 

网络请求与拦截器图解:

 

3、其余次要类的简述

OkHttpClient:通信的客户端,用来统一管理发起请求与解析响应。

Call:Call是一个接口,它是HTTP请求的抽象描述,具体实现类是RealCall,它由CallFactory创建。

Request:请求,封装请求的具体信息,例如:url、header等。

RequestBody:请求体,用来提交流、表单等请求信息。

Response:HTTP请求的响应,获取响应信息,例如:响应header等。

ResponseBody:HTTP请求的响应体,被读取一次以后就会关闭,所以我们重复调用responseBody.string()获取请求结果是会报错的。

Interceptor:Interceptor是请求拦截器,负责拦截并处理请求,它将网络请求、缓存、透明压缩等功能都统一起来,每个功能都是一个Interceptor,所有的Interceptor最终连接成一个Interceptor.Chain,典型的责任链模式实现。

StreamAllocation:用来控制Connections与Streas的资源分配与释放。

RouteSelector:选择路线与自动重连。

RouteDatabase:记录连接失败的Route黑名单。

Dispatcher任务调度器类:

第一行创建了一个Dispatcher任务调度器,它定义三个双向任务队列。

两个异步队列:准备执行的请求队列 readyAsyncCalls、正在运行的请求队列runningAsyncCalls;一个正在运行的同步请求队列runningSyncCalls;
public final class Dispatcher {
private int maxRequests = 64; //最大请求数量
private int maxRequestsPerHost = 5; //每台主机最大的请求数量
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService; //线程池
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
/** 这个线程池没有核心线程,线程数量没有限制,空闲60s就会回收*/
public synchronized ExecutorService executorService() {
if (executorService == null) {
   executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
   new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
  return executorService;
}
}

3.okhttp3涉及的线程池

OkHttp 中的对所有的任务采用 NamedRunnable,约束每个执行单元给出对应的业务名称,以便于线程维护。

1).异步请求线程池-OkHttp Dispatcher
public synchronized ExecutorService executorService() {
if (executorService == null) {
  executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
   new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
 return executorService;
}
• 该线程池与Android下的 Executors.newCachedThreadPool() 比较类似;
• 无任务上限,自动回收闲置60s的线程,适用于大量耗时较短的任务;
• 虽然线程池无任务上限,但是Dispatcher对入口enqueue()进行了把关,最大的异步任务数默认是64,同一个主机默认是5,当然这两个默认值是可以修改的,Dispatcher提供的修改接口;
okHttpClient.dispatcher().setMaxRequests(128);
okHttpClient.dispatcher().setMaxRequestsPerHost(10);
• 通过两个双端队列来维护准备执行的任务和正在执行的任务:Deque<AsyncCall> readyAsyncCalls, Deque<AsyncCall> runningAsyncCalls;
• 在每个任务结束时,都会检查 readyAsyncCalls 是否有任务,在条件满足的情况下,按照先进先出的原则将任务移动到 runningAsyncCalls中,并在线程池中执行; 

2).连接池清理线程池-OkHttp ConnectionPool

ConnectionPool:

1、判断连接是否可用,不可用则从ConnectionPool获取连接,ConnectionPool无连接,创建新连接,握手,放入ConnectionPool。

2、它是一个Deque,add添加Connection,使用线程池负责定时清理缓存。

3、使用连接复用省去了进行 TCP 和 TLS 握手的一个过程。

  • networkInterceptors:用户定义网络拦截器
  • CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据

private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
• 该线程池用来清理长时间闲置的和泄漏的连接;
• 该线程池本身无任务上限,线程闲置60s自动回收;
• 虽然任务无上限,但其通过 cleanupRunning 标记来控制只有一个线程在运行,当连接池中没有连接后才会被重新设置为 false;
void put(RealConnection connection) {
 assert (Thread.holdsLock(this));
 if (!cleanupRunning) {
  cleanupRunning = true;
  executor.execute(cleanupRunnable);
}
connections.add(connection);
}
• 次工作线程会不断地清理,当清理完一遍后超时连接后,根据当前连接池中最近的下一个空闲超时连接计算出一个阻塞时间并阻塞,直到连接池中没有任何连接才结束,并将 cleanupRunning 设为 false;
• 在每次有连接加入连接池时,如果当前没有清理任务运行,会加入一个清理任务到到线程池中执行;
void put(RealConnection connection) {
 assert (Thread.holdsLock(this));
 if (!cleanupRunning) {
 cleanupRunning = true;
 executor.execute(cleanupRunnable);
}
connections.add(connection);
}

3). 缓存整理线程池-OkHttp DiskLruCache
// Use a single background thread to evict entries.
Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true));
• 该线程池用于整理本地请求缓存数据;
• 缓存的整理包含: 达到阀值大小的文件,删除最近最少使用的记录,在有关操作达到一定数量以后对记录进行重建;
• 最大运行线程数1,无需考虑线程安全问题,自动回收闲置60s的线程;

4). HTTP2异步事务线程池-OkHttp Http2Connection
• HTTP2采用了多路复用,因此需要维护连接有效性,本线程池就是用于维护相关的各类HTTP2事务;
• 线程池本身无任务上限,自动回收闲置60s的线程;
• 每一个HTTP2连接都有这么一个线程池存在; 

3.okhttp的拦截器:

1)在配置 OkHttpClient 时设置的 interceptors;


2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;


3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;


4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;


5)负责和服务器建立连接的 ConnectInterceptor;


6)配置 OkHttpClient 时设置的networkInterceptors;


7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。从上述源码得知,不管okhttp有多少拦截器最后都会走,如下方法:
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
调用 chain.proceed(originalRequest); 将request传递进来,从拦截器链里拿到返回结果。那么拦截器Interceptor是干嘛的,Chain是干嘛的呢?继续往下看RealInterceptorChain。

Response getResponseWithInterceptorChain() {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>(); //这是一个List,是有序的
interceptors.addAll(client.interceptors()); //首先添加的是用户添加的全局拦截器
interceptors.add(retryAndFollowUpInterceptor); //错误、重定向拦截器
//桥接拦截器,桥接应用层与网络层,添加必要的头、
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存处理,Last-Modified、ETag、DiskLruCache等
interceptors.add(new CacheInterceptor(client.internalCache()));
//连接拦截器
interceptors.add(new ConnectInterceptor(client));
//从这就知道,通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
//真正访问服务器的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
 return chain.proceed(originalRequest);
}

4:项目中使用到的设计模式分析:

1)外观模式:OkhttpClient类
2) 责任链模式 
3)build模式:Request request = new Request.Builder().url(url)
5.基本使用方法
 ....
6.其他相关

1) HttpClient和HttpConnection区别?

Http Client适用于web浏览器,拥有大量灵活的API,实现起来比较稳定,且其功能比较丰富,提供了很多工具,封装了http的请求头,参数,内容体,响应,还有一些高级功能,代理、COOKIE、鉴权、压缩、连接池的处理。但是,正因此,在不破坏兼容性的前提下,其庞大的API也使人难以改进,因此Android团队对于修改优化Apache Http Client并不积极。(并在Android 6.0中抛弃HttpClient,替换成OkHttp)

HttpURLConnection对于大部分功能都进行了包装,Http Client的高级功能代码会较复杂,另外,HttpURLConnection在Android 2.3中增加一些Https方面的改进(包括Http Client,两者都支持https)。且在Android4.0中增加response cache。当缓存被安装后(调用HttpResponseCache的install()方法),所有的HTTP请求都会满足以下三种情况:

  • 所有的缓存响应都由本地存储来提供。因为没有必要去发起任务的网络连接请求,所有的响应都可以立刻获取到。
  • 视情况而定的缓存响应必须要有服务器来进行更新检查。比如说客户端发起了一条类似于 “如果/foo.png这张图片发生了改变,就将它发送给我” 这样的请求,服务器需要将更新后的数据进行返回,或者返回一个304 Not Modified状态。如果请求的内容没有发生,客户端就不会下载任何数据。
  • 没有缓存的响应都是由服务器直接提供的。这部分响应会在稍后存储到响应缓存中。

在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。 而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。

它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection。 

HttpUrlConnection和okhttp关系?
2)Volley与OkHttp的对比:

Volley:支持HTTPS。缓存、异步请求,不支持同步请求。协议类型是Http/1.0, Http/1.1,网络传输使用的是 HttpUrlConnection/HttpClient,数据读写使用的IO。

OkHttp:支持HTTPS。缓存、异步请求、同步请求。协议类型是Http/1.0, Http/1.1, SPDY, Http/2.0, WebSocket,网络传输使用的是封装的Socket,数据读写使用的NIO(Okio)。            SPDY协议类似于HTTP,但旨在缩短网页的加载时间和提高安全性。SPDY协议通过压缩、多路复用和优先级来缩短加载时间。

五、网络底层框架:OkHttp实现原理

这个库是做什么用的?

网络底层库,它是基于http协议封装的一套请求客户端,虽然它也可以开线程,但根本上它更偏向真正的请求,跟HttpClient, HttpUrlConnection的职责是一样的。其中封装了网络请求get、post等底层操作的实现。

为什么要在项目中使用这个库?
  • OkHttp 提供了对最新的 HTTP 协议版本 HTTP/2 和 SPDY 的支持,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接。
  • 如果 HTTP/2 和 SPDY 不可用,OkHttp 会使用连接池来复用连接以提高效率。
  • OkHttp 提供了对 GZIP 的默认支持来降低传输内容的大小。
  • OkHttp 也提供了对 HTTP 响应的缓存机制,可以避免不必要的网络请求。
  • 当网络出现问题时,OkHttp 会自动重试一个主机的多个 IP 地址。
这个库都有哪些用法?对应什么样的使用场景?

     get、post请求、上传文件、上传表单等等。

这个库的优缺点是什么,跟同类型库的比较?
  • 优点:在上面
  • 缺点:使用的时候仍然需要自己再做一层封装。
这个库的核心实现原理是什么?如果让你实现这个库的某些核心功能,你会考虑怎么去实现?

   OkHttp内部的请求流程:使用OkHttp会在请求的时候初始化一个Call的实例,然后执行它的execute()方法或enqueue()方法,内部最后都会执行到getResponseWithInterceptorChain()方法,这个方法里面通过拦截器组成的责任链,依次经过用户自定义普通拦截器、重试拦截器、桥接拦截器、缓存拦截器、连接拦截器和用户自定义网络拦截器以及访问服务器拦截器等拦截处理过程,来获取到一个响应并交给用户。其中,除了OKHttp的内部请求流程这点之外,缓存和连接这两部分内容也是两个很重要的点,掌握了这3点就说明你理解了OkHttp。

各个拦截器的作用:
  • interceptors:用户自定义拦截器
  • retryAndFollowUpInterceptor:负责失败重试以及重定向
  • BridgeInterceptor:请求时,对必要的Header进行一些添加,接收响应时,移除必要的Header
  • CacheInterceptor:负责读取缓存直接返回(根据请求的信息和缓存的响应的信息来判断是否存在缓存可用)、更新缓存
  • ConnectInterceptor:负责和服务器建立连接
你从这个库中学到什么有价值的或者说可借鉴的设计思想?

    使用责任链模式实现拦截器的分层设计,每一个拦截器对应一个功能,充分实现了功能解耦,易维护。

手写拦截器?
OKhttp针对网络层有哪些优化?
网络请求缓存处理,okhttp如何处理网络缓存的?

Okhttp的子系统层级结构图如下所示:

image

网络配置层: 利用Builder模式配置各种参数,例如:超时时间、拦截器等,这些参数都会由Okhttp分发给各个需要的子系统。

重定向层:负责重定向。  Header拼接层:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。

HTTP缓存层:负责读取缓存以及更新缓存。 连接层:连接层是一个比较复杂的层级,它实现了网络协议、内部的拦截器、安全性认证,连接与连接池等功能,但这一层还没有发起真正的连接,它只是做了连接器一些参数的处理。

数据响应层:负责从服务器读取响应的数据。 在整个Okhttp的系统中,要理解以下几个关键角色:

OkHttpClient:通信的客户端,用来统一管理发起请求与解析响应。

Call:Call是一个接口,它是HTTP请求的抽象描述,具体实现类是RealCall,它由CallFactory创建。

Request:请求,封装请求的具体信息,例如:url、header等。

RequestBody:请求体,用来提交流、表单等请求信息。 Response:HTTP请求的响应,获取响应信息,例如:响应header等。

ResponseBody:HTTP请求的响应体,被读取一次以后就会关闭,所以我们重复调用responseBody.string()获取请求结果是会报错的。

Interceptor:Interceptor是请求拦截器,负责拦截并处理请求,它将网络请求、缓存、透明压缩等功能都统一起来,每个功能都是一个Interceptor,所有的Interceptor最 终连接成一个Interceptor.Chain。典型的责任链模式实现。

StreamAllocation:用来控制Connections与Streas的资源分配与释放。 RouteSelector:选择路线与自动重连。 RouteDatabase:记录连接失败的Route黑名单。

自己去设计网络请求框架,怎么做?
从网络加载一个10M的图片,说下注意事项?
http怎么知道文件过大是否传输完毕的响应?
谈谈你对WebSocket的理解?
WebSocket与socket的区别?
posted on 2023-02-14 11:12  左手指月  阅读(317)  评论(0编辑  收藏  举报