Android---OkHttp详解
OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App,OkHttp 现在几乎已经占据了所有的网络请求操作。RetroFit + OkHttp 实现网络请求似乎成了一种标配。
因此,它也是每个 Android 开发工程师的必备技能。了解其内部实现原理,可以更好的进行功能扩展、封装以及优化。因为是 Http 网络请求的依赖库,所有需要有一定的网络知识基础。
网络请求流程分析
OkHttp 经过几次迭代后,发生了很多变化:
更好的 WebSocke 支持;
更好的 Interceptor 责任链;
最核心的 HttpEngine 也变成了 HttpCodec。
OkHttp的基本使用
OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url(url).build();client.newCall(request).enqueue(new Callback(){@Overridepublic void onFailure(Call call, IOException e){}@Overridepublic void onResponse(Call call, Response response) throw IOException{}
});
除了直接 new OkHttpClient() 之外,还可以使用内部工厂类 Builder 来设置 OkHttpClient。如下所示
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(60, TimeUnit.SECONDS) // 设置超时.addInterceptor(interceptor) // 添加拦截器.proxy(proxy) // 设置请求代理.cache(cache); // 设置缓存策略
OkHttpClient client = builder.build();
请求操作的起点从 OkHttpClient.newCall().enqueue() 方法开始
newCall():这个方法会返回一个 RealCall 类型对象。通过它将网络请求操作添加到网络请求队列中去。

RealCall.enqueue():调用 dispatcher 的入队方法,执行一个网络请求操作。

可以看出,最终的请求操作是委托给 dispatcher 的 enqueue() 方法内实现的。
Dispatcher 是 OkHttpClient 的调度器,是一种门户模式。主要用来实现执行、取消异步请求操作,本质上是内部维护了一个线程池去执行异步操作。并且,在 Dispatcher 内部根据一定的策略,保证最大并发个数、同一 host 主机允许执行请求的线程个数。
Dispatcher 的 enqueue() 方法具体实现,如下

可以看出,实际上就是使用线程池执行了一个 AsyncCall,而 AsyncCall 实现了 Runnable 接口。因此整个操作会在一个子线程中执行。AsyncCall 中的 run() 方法如下

在 run() 方法中执行了另一个 execute() 方法。而真正获取请求结果的方法是在 getResponseWithInterceptorChain() 方法中。其内部是一个拦截器的调用链。具体代码如下

每一个拦截器的作用
BridgeInterceptor:主要对 Request 中的 Head 设置默认值。比如 Content-Type、Keep-Alive、Cookie 等。
CacheInterceptor:负责 HTTP 请求的缓存处理。
ConnectInterceptor:负责建立与服务器地址之间的连接,也就是 TCP 链接。
CallServerInterceptor:负责向服务器发送请求,并从服务器拿到远端数据结果。
在添加上述几个拦截器之前,会调用 client.interceptors,将开发人员设置的拦截器添加到列表当中。对于 Request Head 以及 TCP 链接,我们能控制修改的成分不说很多,所以我们重点分析的是 CacheInterceptor 和 CallServerInterceptor。
1. CacheInterceptor 缓存拦截器
CacheInterceptor 主要做以下几件事情:
a. 根据 Request 获取当前已有缓存的 Response(有可能为 null),并根据获取到的缓存 Response,创建 CacheStrategy 对象。

b. 通过 CacheStrategy 判断当前缓存中的 Response 是否有效(比如是否过期)。
如果缓存 Response 可用,则直接返回。否则调用 chain.proceed() 继续执行下一个拦截器。也就是发送网络请求,从服务器获取远端 Response,具体如下

c. 如果从服务器端成功获取 Response,再判断是否将此 Response 进行缓存操作。代码如下

通过 Cache 实现缓存功能
通过上面分析缓存拦截器的流程可以看出,OkHttp 只是规范了一套策略,但是具体使用何种方式将数据缓存到本地,以及如何从本地缓存中取出数据,都是由开发人员自己定义并实现,并通过 OkHttpClient.Builder 的 cache 方法设置。
OkHttp 提供了一个默认的缓存类 Cache.java,可以在构建 OkHttpClient 时,直接使用 Cache 来实现缓存功能。只需要指定缓存路径以及最大可用空间即可。如下所示

上述代码使用 Android app 内置目录 cache 目录作为缓存路径,并设置缓存最大可用空间为 20M。
实际上,在 Cache 内部使用了 DiskLruCache 实现具体的缓存功能。如下所示

DiskLruCache 最终会以
2. CallServerInterceptor 拦截器
CallServerInterceptor 是 OkHttp 最后一个拦截器,也是 OkHttp 中最核心的网络请求部分。它的 Intercept 方法如下

如上图所示,主要分为两部分。蓝线以上的操作是向服务器端发送请求数据,蓝线以下代表从服务器端获取请求结果并构建 response 对象。
OkHttp 使用扩展
仔细观察上面的代码,在 CallServerInterceptor 中的 Intercept 方法。可用发现,在向服务的发送数据和获取数据都是使用一个 Okio 的框架。
Okio 是 Square 公司打造的另外一个轻量级 IO 库,它是 OkHttp 框架的基石。在构建 Response 时,需要调动 body() 方法传入一个 ResponseBody 对象,ResponseBody 内部封装了对请求结果的流读取操作。可用通过继承并扩展 ResponseBody 的方式获取网络请求的进度。
a. 继承 ResponseBody

其中,progressListener 是一个自定义的进度监听器,通过它向上层汇报网络请求进度。
b. 自定义 progressBarClient

getClient 可以根据项目的不同添加其他共通设置,比如 timeout 时间,DNS、Log日志 interceptor 等。
getProgressBarClient 通过添加一个拦截器,并且在 intercept 方法中将自定义的 ProgressResponseBody 传个 body 方法。当通过 getProgressBarClient 发送网络请求时,OkHttpClient 从服务端获取到数据之后,会不断调用 ProgressResponseBody 中的 source 方法。然后通过 progressListener 向上层通知请求进度的结果。
c. 实践拓展-Picasso
我们可以将上面实现的 ProgressBarClient 用于 Square 公司另一个请求库--Picasso。Picasso 是 Square 公司研发用来从网络端获取图片数据的依赖库,内部实质上是使用 OkHttp 来实现请求操作的。因此我们可以将 ProgressBarClient 替换 OkHttpClient,这样就能获取下载图片的进度。代码如下
后续只要通过 GetPicasso 方法即可获得一个自带下载进度的 Picasso 对象。因为,OkHttp、Picasso 和 Okio 都来自 Square 公司。
总结
主要分析了 OkHttp 的源码实现:
OkHttp 内部是一个门户模式,所有的下发工作都是通过一个门户 Dispatcher 来进行分发。
在网络请求阶段通过责任链模式,链式的调用各个拦截器的 intercept 方法。重点介绍了2个比较重要的拦截器:CacheInterceptor 和 CallServerInterceptor。它们分别用来做请求缓存和执行网络请求操作。
在理解源码实现的基础上,对 OkHttp 的功能进行了一些扩展,实现了网络请求进度的实现。
相关文章:
Android---OkHttp详解
OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App,OkHttp 现在几乎已经占据了所有的网络请求操作。RetroFit OkHttp 实现网络请求似乎成了一种标配。 因此&…...
向某文件中逐秒追加带序号输入当前时间 fgets fputs fprintf sprintf
//向某文件中逐秒追加带序号输入当前时间 #include<stdio.h> #include<stdlib.h> #include<time.h> #include<string.h> #include <unistd.h> int main(int argc, char const *argv[]) { time_t tv; // time(&tv);//法1:获取秒数 …...
同为科技(TOWE)机架PDU产品在IDC数据中心机房建设中的应用
当今社会互联网发展迅速, 随着带宽需求的提升, 网络的保密性、安全性的要求就越来越迫切。PDU(Power Distribution Unit) 是 PDU具备电源分配和管理功能的电源分配管理器。PDU电源插座是多有设备运行的第一道也是最为密切的部件, PDU的好坏直…...
Elasticsearch学习笔记
1.核心概念 bucket: 一个数据分组(类似于sql group by以后的数据)metric:对bucket执行的某种聚合分析的操作,比如说求平均值,最大值,最小值。一些系列的统计方法(类似 select count(1) MAX MIN AVG) 请…...
Java框架随笔
Maven面试题 Myabtis面试题 文章目录 Maven面试题Myabtis面试题 1、简述Spring Boot的启动流程2、如何理解Bean的生命周期3、MyBatis的主要功能4、MyBatis的组成部分5、MyBatis的动态SQL 1、简述Spring Boot的启动流程 Spring Boot的启动流程可以分为以下几个步骤:…...
自然语言处理基础——词表示
词表示 把自然语言中最基本的语言单元——词转换为机器能够理解的 词表示能完成以下两个能力 词相似度计算 词与词之间语义的关系 近义词&上位词 使用近义词或上位词表示的问题 遗漏差异 遗漏新的释义 带有主观性 数据吸收 需要大量人工构建 One-Hot Representation …...
2023年9月青少年软件编程(C 语言) 等级考试试卷(七级)
青少年软件编程(C/C)7级等级考试真题试卷(2023年9月) 编程题第 1 题 红与黑(2023.9) 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,…...
鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统项目背景
鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…...
apache httpd 换行解析漏洞
原理 Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。 漏洞编号 cve-2017-15715 环境…...
【设计模式】工厂模式
工厂模式 1.什么是工厂模式 它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象…...
C++二分算法的应用:寻找峰值原理、源码及测试用例
说明 此文是课程https://edu.csdn.net/course/detail/38771 的讲义。 源码下载:https://download.csdn.net/download/he_zhidan/88458478 题目 长度为n的数组nums,请返回任意一峰值的索引。符合以下条件之一i便是峰值的索引。 n等于1 i等于0 n>…...
外汇天眼:本周无牌裸奔平台名单出炉,你踩“坑”了么?!!
监管信息早知道!外汇天眼将每周定期公布监管牌照状态发生变化的交易商,以供投资者参考,规避投资风险。如果平台天眼评分过高,建议投资者谨慎选择,因为在外汇天眼评分高不代表平台没问题! 以下是监管牌照发生…...
10 读写锁ReentrantReadWriteLock
1 介绍 为什么要使用读写锁? 需要高并发读取和较低并发写入的应用程序,降低锁的粒度,提高系统性能 使用场景: 读多写少的共享资源 缓存管理:读 >> 写,控制多个线程同时读缓存,需要刷新o…...
laravel队列
laravel redis队列 1、创建job队列任务 php artisan make:job StoreUser执行上述命令后,会生成app/Jobs/StoreUser.php文件,编辑文件内容如下: <?phpnamespace App\Jobs;use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queu…...
【计算机网络】TCP 协议的相关特性
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的协议。以下是TCP协议的相关特性: 可靠性:TCP通过确认和重传机制保证数据的可靠传输。 面向连接:TCP在传输数据前需要先建立连接。连接的建立过程包括三次握手…...
[软件安装] tmux安装及相关事项
tmux安装及相关事项 tmux是一个终端复用工具,可以在单个终端窗口中同时运行多个终端会话。安装tmux可以提高工作效率,使命令行操作更加方便。 1. 安装tmux: 在Linux系统下,可以使用包管理器来安装tmux,比如在Ubuntu…...
leetcode 887 ——扔鸡蛋
题目大意: 你有k个鸡蛋,对n层楼的建筑,请确认在f层扔鸡蛋鸡蛋恰好不会破碎的最少次数(f满足 0 < f < n)。 方法一: 状态:即会发生变化的量,很明显有两个,当前拥有…...
自动化运维ansible(role)
一、role的介绍 1、Roles称为角色,本质上是为简化playbook配置文件而产生的一种特殊的方法。 2、简单来说,roles就是将原本在一个yaml中的文件进行规则化分散,封装到不同的目录下,从而简化playbook的yaml配置文件大小。从其实现方…...
linux命令笔记
创建文件夹 sudo mkdir 文件夹名vim笔记 vim的查找和退出查找 进入vim 按/ 输入内容即可查找 按enter结束查找vim创建文件并在里面写东西 比如创建文件为 hello.cpp vim hello.cpp查看所有文件 # 查看所有文件,并以列表的形式查看,显示出文件大小 …...
2.3.C++项目:网络版五子棋对战之实用工具类模块的设计
文章目录 一、实用工具类模块(一)功能 二、设计和封装(一)日志宏封装(二)mysql_util封装(三)Jsoncpp-API封装(四)file_util封装(五)st…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
