编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Okhttp基本用法和流程分析

wxchong 2024-06-10 17:02:33 开源技术 13 ℃ 0 评论

本文基于compile 'com.squareup.okhttp3:okhttp:3.0.1'

OkHttp的时序图

1.基本用法


这里我是将Okhttp和Retrofit一起使用的

Okhttp执行底层的网络请求,Retrofit负责网络调度,有关Retrofit的更多可参考我的另一篇博文:Retrofit基本用法和流程分析

1.初始化

public static void init() {

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

logging.setLevel(HttpLoggingInterceptor.Level.BODY);

sClient = new OkHttpClient.Builder()

.retryOnConnectionFailure(true)

.connectTimeout(15, TimeUnit.SECONDS)

.readTimeout(300, TimeUnit.SECONDS)

.writeTimeout(300, TimeUnit.SECONDS)

.cache(new Cache(Constants.HTTP_CACHE_DIR, Constants.CACHE_SIZE))

.addInterceptor(logging)//第三方的日志拦截器

.addInterceptor(appIntercepter)//自定义的应用拦截器

.addNetworkInterceptor(netIntercepter)//自定义的网络拦截器

.build();

}

2.添加应用拦截器

 //应用拦截器:主要用于设置公共参数,头信息,日志拦截等,有点类似Retrofit的Converter
 private static Interceptor appIntercepter = new Interceptor() {
 @Override
 public Response intercept(Chain chain) throws IOException {
 Request request = processRequest(chain.request());
 Response response = processResponse(chain.proceed(request));
 return response;
 }
 };
 //访问网络之前,处理Request(这里统一添加了Cookie)
 private static Request processRequest(Request request) {
 String session = CacheManager.restoreLoginInfo(BaseApplication.getContext()).getSession();
 return request
 .newBuilder()
 .addHeader("Cookie", "JSESSIONID=" + session)
 .build();
 }
 //访问网络之后,处理Response(这里没有做特别处理)
 private static Response processResponse(Response response) {
 return response;
 }

这里再奉上一个缓存拦截器:离线读取本地缓存,在线获取最新数据(读取单个请求的请求头,亦可统一设置)。

这里用到了一个网络状态工具类

 //应用拦截器:设置缓存策略
 private static Interceptor cacheIntercepter = new Interceptor() {
 @Override
 public Response intercept(Chain chain) throws IOException {
 Request request = chain.request();
 //无网的时候强制使用缓存
 if (NetUtil.getNetState() == NetUtil.NetState.NET_NO) {
 request = request.newBuilder()
 .cacheControl(CacheControl.FORCE_CACHE)
 .build();
 }
 Response response = chain.proceed(request);
 //有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置
 if (NetUtil.getNetState() != NetUtil.NetState.NET_NO) {
 String cacheControl = request.cacheControl().toString();
 return response.newBuilder()
 .header("Cache-Control", cacheControl)
 .removeHeader("Pragma")
 .build();
 } else {
 int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
 return response.newBuilder()
 .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
 .removeHeader("Pragma")
 .build();
 }
 }
 };

OkHttp3中有一个Cache类是用来定义缓存的,此类详细介绍了几种缓存策略,具体可看此类源码。

  • noCache :不使用缓存,全部走网络
  1. noStore : 不使用缓存,也不存储缓存
  2. onlyIfCached : 只使用缓存
  3. maxAge :设置最大失效时间,失效则不使用
  4. maxStale :设置最大失效时间,失效则不使用
  5. minFresh :设置最小有效时间,失效则不使用
  6. FORCE_NETWORK : 强制走网络
  7. FORCE_CACHE :强制走缓存

关于max-age和max-stale

max-stale在请求头设置有效,在响应头设置无效。

max-stale和max-age同时设置的时候,缓存失效的时间按最长的算。

我这里借用了别人的一个测试(太懒了,你有兴趣可以自己测试下):

测试结果:

在请求头中设置了:Cache-Control: public, max-age=60,max-stale=120,响应头的Cache-Control和请求头一样。

  1. 在第一次请求数据到一分钟之内,响应头有:Cache-Control: public, max-age=60,max-stale=120
  2. 在1分钟到3分钟在之间,响应头有:Cache-Control: public, max-age=60,max-stale=120
  3. Warning: 110 HttpURLConnection "Response is stale"
  4. 可以发现多了一个Warning。
  5. 三分钟的时候:重新请求了数据,如此循环,如果到了重新请求的节点此时没有网,则请求失败。
  6. 另外关于缓存有一个rxcache也可以试试。

3.添加网络拦截器

 //网络拦截器:主要用于重试或重写
 private static Interceptor netIntercepter = new Interceptor() {
 @Override
 public Response intercept(Chain chain) throws IOException {
 Request request = chain.request();
 Response response = chain.proceed(request);
 int tryCount = 0;
 while (!response.isSuccessful() && tryCount < sMaxTryCount) {
 tryCount++;
 response = chain.proceed(request);
 }
 return response;
 }
 };

关于OkHttp的拦截机制,我觉得这是OkHttp最牛逼的地方之一!

先给大家看个概览图,之后会在OkHttp的特性中详细介绍下。

OkHttp的拦截机制

4.简单的异步请求

 private static void testAsync() {
 Request request = new Request.Builder()
 .url("http://baidu.com")
 .build();
 sClient.newCall(request).enqueue(new Callback() {
 @Override
 public void onFailure(Call call, IOException e) {
 LogUtil.print(e.getMessage());
 }
 @Override
 public void onResponse(Call call, Response response) throws IOException {
 if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 Headers responseHeaders = response.headers();
 for (int i = 0, size = responseHeaders.size(); i < size; i++) {
 LogUtil.print(responseHeaders.name(i) + ": " + responseHeaders.value(i));
 }
 LogUtil.print(response.body().string());
 }
 });
 }

2.OkHttp的特性


Q1:为什么使用OkHttp?

  • 支持HTTP2/SPDY黑科技
  1. socket自动选择最好路线,并支持自动重连
  2. 拥有自动维护的socket连接池,减少握手次数
  3. 拥有队列线程池,轻松写并发
  4. 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
  5. 基于Headers的缓存策略

Q2:应用拦截器和网络拦截器的区别?

addInterceptor:设置应用拦截器,主要用于设置公共参数,头信息,日志拦截等

addNetworkInterceptor:设置网络拦截器,主要用于重试或重写

OkHttp的应用拦截器和网络拦截器

例如:初始请求http://baidu.com 会重定向1次到https://baidu.com 。

则应用拦截器会执行1次,返回的是https的响应

网络拦截器会执行2次,第一次返回http的响应,第二次返回https的响应

应用拦截器:

  1. 不需要担心中间过程的响应,如重定向和重试.
  2. 总是只调用一次,即使HTTP响应是从缓存中获取.
  3. 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
  4. 允许短路而不调用 Chain.proceed(),即中止调用.
  5. 允许重试,使 Chain.proceed()调用多次.

网络拦截器

  1. 能够操作中间过程的响应,如重定向和重试.
  2. 当网络短路而返回缓存响应时不被调用.
  3. 只观察在网络上传输的数据.
  4. 携带请求来访问连接.

3.OkHttp的流程分析


二话不说,先上几张图:

OkHttp的请求流程

OkHttp的总体设计

1.先看初始化时,通过建造者模式创建OkHttpClient

2.再看执行请求时,创建Request的方法

3.再看异步请求Request的方法,先通过newCall(),将Request转成Call

4.然后加入请求队列

5.Call.enqueue()是抽象方法,实现在这:

6.调用了Dispatcher.enqueue(),通过一个线程池来执行,超过最大请求数后则先加入准备请求的队列中

7.这里的call是一个AsynCall,继承自NamedRunnable

8.在NamedRunnable的run()方法中,调用了execute()抽象方法,于是我们去找它的实现类AsyncCall的execute()方法

9.调用了getResponseWithInterceptorChain()方法

10.调用了ApplicationInterceptorChain.proceed(),如果有其他的应用拦截器的话,就会遍历拦截器集合,执行每一个拦截的intercept()方法

而通过前面的自定义应用拦截器,我们知道intercept()中其实也会调用proceed(),这样迭代多次后,最终还是会执行getResponse方法()

13.getResponse()这个方法有点长,一次截图截不完

14.调用httpEngine.sendRequest()方法,这个方法有点长,一次截图截不完

15.先从 Cache 中判断当前请求是否可以从缓存中返回

16.没有Cache则连接网络

17.调用streamAllocation.newStream()

18.寻找可用的socket连接

19.连接到socket连接层

20.创建了FramedConnection的实例

21.调用了Reader的execute()

写在最后:

码字不易看到最后了,那就点个关注呗,只收藏不点关注的都是在耍流氓!

关注并私信我“架构”,免费送一些Java架构资料,先到先得!记得转发哦!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表