OkHttp: 拦截器和事件监听器

文章目录

    • 1. 拦截器
      • 1. 拦截器链
      • 2. 实际案例
        • 1. 注册为应用拦截器
        • 2. 注册为网络拦截器
      • 3. 如何选择用哪种拦截器
        • 1. 应用拦截器
        • 2. 网络层拦截器
        • 3. 重写请求
        • 4. 重写响应
      • 4. 可用性
    • 2. 事件监听器
      • 1. 请求的生命周期
      • 2. EventListener使用案例
      • 3. EventListener.Factory
      • 4. 调用失败的请求

1. 拦截器

拦截器是一种强大的机制,可以用来监测、重写、重试调用。下面是一个简单的例子,用来打印请求的输入和输出。

class LoggingInterceptor implements Interceptor {@Override public Response intercept(Interceptor.Chain chain) throws IOException {Request request = chain.request();long t1 = System.nanoTime();logger.info(String.format("Sending request %s on %s%n%s",request.url(), chain.connection(), request.headers()));Response response = chain.proceed(request);long t2 = System.nanoTime();logger.info(String.format("Received response for %s in %.1fms%n%s",response.request().url(), (t2 - t1) / 1e6d, response.headers()));return response;}
}

1. 拦截器链

拦截器可以形成一个拦截器链, 拦截器分为应用层拦截器、网络层拦截器,如下图所示:
在这里插入图片描述

2. 实际案例

假设我们访问的是http://www.publicobject.com/helloworld.txt,它实际上会有一个302跳转到https://publicobject.com/helloworld.txt, OkHttp会自动完成跳转。

我们用上面的LoggingInterceptor做为例子来讲解应用拦截器和网络层拦截器的区别。

1. 注册为应用拦截器
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User-Agent", "OkHttp Example").build();Response response = client.newCall(request).execute();
response.body().close();

输出只会有一个Request,一个Response,内部的跳转过程没有感知。以下是输出内容:

INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp ExampleINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive
2. 注册为网络拦截器
OkHttpClient client = new OkHttpClient.Builder().addNetworkInterceptor(new LoggingInterceptor()).build();Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User-Agent", "OkHttp Example").build();Response response = client.newCall(request).execute();
response.body().close();

输出会感知每一个Request、Response对象。以下为输出内容:

INFO: Sending request http://www.publicobject.com/helloworld.txt on Connection{www.publicobject.com:80, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=none protocol=http/1.1}
User-Agent: OkHttp Example
Host: www.publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzipINFO: Received response for http://www.publicobject.com/helloworld.txt in 115.6ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/html
Content-Length: 193
Connection: keep-alive
Location: https://publicobject.com/helloworld.txtINFO: Sending request https://publicobject.com/helloworld.txt on Connection{publicobject.com:443, proxy=DIRECT hostAddress=54.187.32.157 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA protocol=http/1.1}
User-Agent: OkHttp Example
Host: publicobject.com
Connection: Keep-Alive
Accept-Encoding: gzipINFO: Received response for https://publicobject.com/helloworld.txt in 80.9ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

网络层拦截器还能给我们提供更多信息,比如Accept-Encoding、Connection这些OkHttp帮我们自动添加的头信息。

3. 如何选择用哪种拦截器

1. 应用拦截器
  • 不关心重定向、重试
  • 永远只显示一次,即使是从缓存中读取结果
  • 只关心应用的原始意图。不关心OkHttp自动添加的头,比如If-None-Match等
  • 允许短路,不调用Chain.process(request)
  • 允许重试,调用多次Chain.process(request)
2. 网络层拦截器
  • 允许修改和操作中间的请求结果和状态
  • 从缓存中读取时,不调用拦截器
  • 关心中间过程
  • 访问请求的Connection对象
3. 重写请求

拦截器可以添加、删除、修改HTTP头,甚至可以改变请求体。比如你可以在拦截器内对请求体做压缩(如果你知道Web服务器支持的话)。

final class GzipRequestInterceptor implements Interceptor {@Override public Response intercept(Interceptor.Chain chain) throws IOException {Request originalRequest = chain.request();if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {return chain.proceed(originalRequest);}Request compressedRequest = originalRequest.newBuilder().header("Content-Encoding", "gzip").method(originalRequest.method(), gzip(originalRequest.body())).build();return chain.proceed(compressedRequest);}private RequestBody gzip(final RequestBody body) {return new RequestBody() {@Override public MediaType contentType() {return body.contentType();}@Override public long contentLength() {return -1; // We don't know the compressed length in advance!}@Override public void writeTo(BufferedSink sink) throws IOException {BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));body.writeTo(gzipSink);gzipSink.close();}};}
}
4. 重写响应

重写响应可以重写响应的HTTP头、响应内容等,一般来说是不推荐的,可能会违反直觉。

比如为响应自动添加缓存头。

private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {@Override public Response intercept(Interceptor.Chain chain) throws IOException {Response originalResponse = chain.proceed(chain.request());return originalResponse.newBuilder().header("Cache-Control", "max-age=60").build();}
};

4. 可用性

需要okhttp 2.2以后的版本,不可以和OkUrlFactory一起工作,不能在Retrofit 1.8及以下版本使用。

2. 事件监听器

你可以通过事件知道OkHttp的内部运行状态。通过时间我们可以监控:

  • HTTP请求的大小、频率
  • 这些请求对应的网络性能

(这里提及的API还不是最终版的API,OkHttp 3.9里这个API只是非稳定的预览版,预计在3.10、3.11会稳定)

你可以通过继承EventListener并覆盖你感兴趣的事件方法来获取通知。

1. 请求的生命周期

一次普通的请求完成,会触发以下事件:
在这里插入图片描述

2. EventListener使用案例

class PrintingEventListener extends EventListener {private long callStartNanos;private void printEvent(String name) {long nowNanos = System.nanoTime();if (name.equals("callStart")) {callStartNanos = nowNanos;}long elapsedNanos = nowNanos - callStartNanos;System.out.printf("%.3f %s%n", elapsedNanos / 1000000000d, name);}@Override public void callStart(Call call) {printEvent("callStart");}@Override public void callEnd(Call call) {printEvent("callEnd");}@Override public void dnsStart(Call call, String domainName) {printEvent("dnsStart");}@Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {printEvent("dnsEnd");}...
}

EventListener会被所有Call共享,所以EventListener本身不能是有状态的,如果需要的话,采用EventListener.Factory

3. EventListener.Factory

Factory可以让相同的Call共用一个EventListener对象,也可以只是随机选一部分Call来监听

class MetricsEventListener extends EventListener {private static final Factory FACTORY = new Factory() {@Override public EventListener create(Call call) {if (Math.random() < 0.10) {return new MetricsEventListener(call);} else {return EventListener.NONE;}}};...
}

4. 调用失败的请求

如果是连接阶段,触发事件connectFailed(),否则触发callFailed()。失败发生的时候,有可能存在调用了start方法,但是没有调用end方法的情况。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/219293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】28. 找出字符串中第一个匹配项的下标 【字符串单模匹配:KMP算法】

题目链接 Python3 直觉解法 class Solution:def strStr(self, haystack: str, needle: str) -> int:pn, ph 0, 0n len(needle) h len(haystack)while ph < h:if haystack[ph] needle[pn]:if pn n-1: # 1234 123return ph - len(needle) 1else: pn 1ph 1else:…

【LeetCode刷题】-- 163.缺失的区间

163.缺失的区间 class Solution {public List<List<Integer>> findMissingRanges(int[] nums, int lower, int upper) {List<List<Integer>> res new ArrayList<>();for(int num : nums){if(lower < num){res.add(Arrays.asList(lower,num -…

c语言函数与指针

//本文有待补充。 一、函数 1.函数的定义 如果程序的逻辑比较复杂、代码量比较大&#xff0c;或者重复性功能比较多&#xff0c;那么全部写在主函数里就会显得十分冗长和杂乱。为了使代码更加简洁、思路更加清晰&#xff0c;C语言提供了”函数“。函数是一个实现一定功能的语…

dockerfile创建镜像 lNMP+wordpress

dockerfile创建镜像 lNMPwordpress nginx dockernginx mysql dockermysql php dockerphp nginx vim nginx.conf vim Dockerfile docker network create --subnet172.17.0.0/16 --opt "com.docker.network.bridge.name""docker1" mynetwork docker buil…

最新Applestore建立其他地区账号简单快捷一看就会

1、首先打开创建appleid网站 2、点击创建你的Apple ID开始创建&#xff08;这里以美国为例&#xff09; 电话号码可以填大陆手机号即可 这两个选项建议不要勾选 3、更改付款方式 3.1点击付款与配送 3.2添加付款方式&#xff0c;这里是最重要的一步&#xff0c;传统方法已经无法…

Mac电脑投屏AirServer 2024怎么下载安装激活许可期限

对于那些想要将 iPhone、iPad 或其他 iOS 设备上的小屏幕镜像到计算机上的大屏幕的人来说&#xff0c;AirPlay 是一个很好的工具。 基于此&#xff0c;AirServer 非常需要将您的 Mac 或 PC 变成 AirPlay 设备。 但是如何使用计算机上的设置对 iPhone 等 iOS 设备进行屏幕镜像&a…

Ubuntu 22安装PHP环境

参考博客为《练习 0&#xff08;2/2&#xff09;&#xff1a;Ubuntu 环境下安装PHP&#xff08;PHP-FPM&#xff09;》和《原生态Ubuntu部署LAMP环境 PHP8.1MySQLApache》 sudo apt-get install -y php7.4想要安装php7.4&#xff0c;发现安装的是php8.1。 完成如下图&#xf…

超声波测距HC-SR04模块的简单应用

文章目录 一、HC-SR04HC-SR04是什么&#xff1f;HC-SR04测距的原理 二、使用步骤1.硬件最远探测距离调节硬件连接 2.软件1.初始化配置代码如下&#xff08;示例&#xff09;&#xff1a;引脚初始化定时器初始化 2.引脚输入输出配置代码如下&#xff08;示例&#xff09;&#x…

【linux系统】用户功能与权限详细总结

前言 菜某的笔记总结&#xff0c;有错误还请指正。 linux用户的概念与root用户 这么理解&#xff1a;一台电脑有多个操作者&#xff0c;每个操作者只能无限制操作自己文件夹中的东西&#xff0c;其他地方的操作需要给与相应权限才能操作。 root用户&#xff1a;就是最高级的…

【Java系列】详解多线程(二)——Thread类及常见方法(上篇)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一…

Proxmox创建CentOS虚拟机

文章目录 下载ISO安装文件上传创建虚拟机启动虚拟机设置DNS CentOS配置国内安装源备份原有安装源下载更新国内源清理yum缓存制作新配置文件缓存 下载ISO安装文件 下载地址&#xff1a;https://www.xitongzhijia.net/ 也可去官网进行下载 上传 下面介绍直接通过页面上传&…

【论文阅读笔记】M3Care: Learning with Missing Modalities in Multimodal Healthcare Data

本文介绍了一种名为“MCare”的模型&#xff0c;旨在处理多模态医疗保健数据中的缺失模态问题。这个模型是端到端的&#xff0c;能够补偿病人缺失模态的信息&#xff0c;以执行临床分析。MCare不是生成原始缺失数据&#xff0c;而是在潜在空间中估计缺失模态的任务相关信息&…

idea一些报错

java: 非法字符: \ufeff 使用IDEA修改文件编码 在IDEA右下角&#xff0c;将编码改为GBK&#xff0c;再转为UTF-8&#xff0c;重新启动项目。具体步骤如下&#xff1a; 在IDEA右下角找到UTF-8字样的编码格式设计项&#xff0c;点击选择第一项GBK&#xff0c;然后Convert&#xf…

JVM虚拟机系统性学习-对象的创建流程及对象的访问定位

对象的创建流程与内存分配 对象创建流程如下&#xff1a; Java 中新创建的对象如何分配空间呢&#xff1f; new 的对象先放 Eden 区&#xff08;如果是大对象&#xff0c;直接放入老年代&#xff09;当 Eden 区满了之后&#xff0c;程序还需要创建对象&#xff0c;则垃圾回收…

Linux的五种IO模型

众所周知&#xff0c;出于对 OS 安全性的考虑&#xff0c;用户进程是不能直接操作 I/O 设备的。必须通过系统调用请求操作系统内核来协助完成 I/O 动作。 下图展示了 Linux I/O 的过程。 操作系统内核收到用户进程发起的请求后&#xff0c;从 I/O 设备读取数据到 kernel buff…

【超详细】创建vue3+ts项目(引入ElementPlus、Axios)

目录 前言1、使用vue脚手架创建项目1.1检查vue版本1.2 使用vue脚手架创建项目 2、删除项目多余文件&#xff0c;修改配置项目2.1、删除以下文件2.1、在views下创建index文件2.2、修改router/index.ts路由文件&#xff1a;2.3、修改App.vue文件&#xff1a;2.4、初始化页面样式以…

windows上抓包出现大包未分片以及关闭tso方法

wireshark抓包中会有大数据包&#xff08;未分片包&#xff09;和ip校验和不对的包&#xff0c;问题根因在目前很多电脑网卡支持TSO和将校验和计算到网卡上&#xff0c;导致抓出数据包未分片 详细文章看: https://www.cnblogs.com/charlieroro/p/11363336.html 目前很多网卡已…

业务代码-整合框架-存储-缓存常见错误详解一

一. java空指针和异常&#xff1a; 1.什么是空指针异常&#xff08;java.lang.NullPointException)&#xff1a; 1.1常见的空指针异常案例&#xff1a; public class WhatIsNpe {public static class User {private String name;private String[] address;public void print…

项目一 分析并设计学生管理数据库

项目一 分析并设计学生管理数据库 1&#xff0c;做好管理数据库的知识准备 1.1&#xff0c;初识数据库 **1&#xff0c;DBMS&#xff1a;**数据库管理系统(Database Management System)。数据库 是通过DBMS创建和操作的 容器。 **2&#xff0c;DB&#xff1a;**数据库(data…

004 Windows NTFS文件夹权限

一、NTFS文件权限&#xff1a; NTFS&#xff08;New Technology File System&#xff09;是Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式&#xff0c;提供长文件名、数据保护和恢复&#xff0c;能通过目录和文件许可…