Android---OkHttp详解

news2024/11/17 0:04:15

OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App,OkHttp 现在几乎已经占据了所有的网络请求操作。RetroFit + OkHttp 实现网络请求似乎成了一种标配。

因此,它也是每个 Android 开发工程师的必备技能。了解其内部实现原理,可以更好的进行功能扩展、封装以及优化。因为是 Http 网络请求的依赖库,所有需要有一定的网络知识基础。

网络请求流程分析

OkHttp 经过几次迭代后,发生了很多变化:

\bullet 更好的 WebSocke 支持;

\bullet 更好的 Interceptor 责任链;

\bullet 最核心的 HttpEngine 也变成了 HttpCodec。

OkHttp的基本使用

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
        .url(url)
        .build();

client.newCall(request).enqueue(new Callback(){

    @Override
    public void onFailure(Call call, IOException e){}
    @Override
    public 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() 方法中。其内部是一个拦截器的调用链。具体代码如下

每一个拦截器的作用

\bullet BridgeInterceptor:主要对 Request 中的 Head 设置默认值。比如 Content-Type、Keep-Alive、Cookie 等。

\bullet CacheInterceptor:负责 HTTP 请求的缓存处理。

\bullet ConnectInterceptor:负责建立与服务器地址之间的连接,也就是 TCP 链接。

\bullet 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 的源码实现:

\bullet OkHttp 内部是一个门户模式,所有的下发工作都是通过一个门户 Dispatcher 来进行分发。

\bullet 在网络请求阶段通过责任链模式,链式的调用各个拦截器的 intercept 方法。重点介绍了2个比较重要的拦截器:CacheInterceptorCallServerInterceptor。它们分别用来做请求缓存执行网络请求操作。

\bullet 在理解源码实现的基础上,对 OkHttp 的功能进行了一些扩展,实现了网络请求进度的实现。

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

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

相关文章

【每日一题】—— 最大素因子

🌏博客主页:PH_modest的博客主页 🚩当前专栏:每日一题 💌其他专栏: 🔴 每日反刍 🟡 C跬步积累 🟢 C语言跬步积累 🌈座右铭:广积粮,缓称…

基于springboot实现4S店车辆管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现4S店车辆管理系统演示 摘要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生&am…

PC性能监测工具,您不可或缺的好帮手

前言 在计算机使用过程中,常有人会问:为什么我的CPU利用率接近100%?为什么可用内存不断减少? 幸运的是,Windows性能工具包为我们提供了帮助。是什么应用程序的锅,我们使用该性能工具一探究竟。 一、 Win…

国内在线协作工具有哪些?

随着信息技术的快速发展和互联网的普及,在线协作工具成为了现代团队和企业不可或缺的一部分。国内市场上涌现出许多优秀的在线协作工具,为团队协作提供了高效、便捷的解决方案。 在本文中,我们将向大家推荐3款国内备受赞誉的在线协作工具&…

[NSSRound#13 Basic] 刷题记录

文章目录 [NSSRound#13 Basic]flask?jwt?[NSSRound#13 Basic]ez_factors[NSSRound#13 Basic]flask?jwt?(hard)[NSSRound#13 Basic]信息收集[NSSRound#13 Basic]MyWeb [NSSRound#13 Basic]flask?jwt? 考点:session伪造 打开题目,想注册admin发现不行…

【C++进阶之路】IO流

文章目录 一、C语言的IO1.键盘与显示屏2. 文件与内存3.字符串与内存 二、CIO1.iostream1.1基本使用1.2operator bool 2. fstream2.1二进制的文件读写2.2字符串的文件读写 3. sstream3.1序列化与反序列化3.2拼接字符串3.3将数据类型转换为字符串 总结 一、C语言的IO 1.键盘与显…

企业知识库软件,快速构建企业知识分享与团队协同的软件

企业知识库是一种特殊的在线协同文档工具,支持包括FAQ、文档、视频、知识图谱等。从本质上讲,它是基于企业知识库软件从而实现内部或外部知识的沉淀、集合、更新、共享等,能为员工或客户提供常见问题的标准回答。 今天我就基于HelpLook &…

STP、堆叠与VRRP如何使用

✍ STP生成树用在哪里? ✍ STP和堆叠有什么区别? ✍ VRRP双网关热备份如何部署? --- 通过交换机组成网络是局域网,连接终端设备的交换机就是接入层交换机。 --- 如上组网结构单一,不需要网工。 容易发生单点故障&…

wireshark数据包内容查找功能详解

wireshark提供通过数据包特征值查找具体数据包的功能,具体查找功能如下, (1)选择查找目标区域(也就是在哪里去匹配特征值) 如下图,【分组列表】区域查找指的是在最上方的数据包列表区域查找&…

msvcr110.dll丢失的解决方法介绍,教你如何快速修复问题

在计算机系统中,DLL(动态链接库)是一种非常重要的资源。它们包含了可被多个程序共享的代码和数据。其中,MSVCR110.dll就是Visual Studio 2012的一个组件。然而,有时候我们可能会遇到“msvcr110.dll丢失”的问题&#x…

2023年传媒行业中期策略 AIGC从三个不同层次为内容产业赋能

基本面和新题材共振,推动传媒互联网行情上涨 AIGC 概念带动,传媒板块领涨 A 股 2023 年第一个交易日(1 月 3 日)至 6 月 2 日,申万传媒指数区间涨幅高达 48.38%,同时期沪深 300 跌幅为 0.25%,…

【Javascript】构造函数的参数写法

目录 写法一(固定参数): 写法二(对象类型的参数) 写法一(固定参数): 如果参数与参数的值不对应 写法一 要求位置严格对应,明确知道对象的属性 写法二(对象类…

JavaScript从入门到精通系列第二十二篇:JavaScript中的toString方法和JavaScript中的垃圾回收

文章目录 一:toString方法 1:怪异的返回值[object Object] 2:打印对象成为一个JSON 二:垃圾回收(GC) 1:垃圾回收概念 2:JS当中的垃圾回收机制 3:JS中的垃圾回收算…

CyclicBarrier线程同步

关于作者: CSDN内容合伙人、技术专家, 从零开始做日活千万级APP,带领团队单日营收超千万。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业化变现、人工智能等,希望大家多多支持。 目录 一、导读二、概览…

数据结构与算法—双向链表

目录 一、链表的分类 二、双向链表原理 三、实现双向链表 1、声明链表结构体 2、初始化链表 3、创建新节点 4、打印链表 5、头插&尾插 头插 尾插 6、头删&尾删 头删 尾删 7、 查找节点 8、指定节点前插入 9、删除指定节点 10、销毁链表 完整版 L…

Java三层架构、表现层-业务层-持久层

三层架构 什么是 Java 三层架构 三层架构是指:视图层view(表现层),服务层service(业务逻辑层),持久层Dao(数据访问层), Java的三层架构是指将Java程序分为三…

会议剪影 | 思腾合力携AI服务器亮相PRCV 2023,并作主题演讲

第六届中国模式识别与计算机视觉大会(PRCV 2023)于2023年10月13日至15日在厦门国际会议中心酒店举办。本届会议主题为“相约鹭岛,启智未来”。 会议旨在汇聚国内国外模式识别和计算机视觉理论与应用研究的广大科研工作者及工业界同行&#xf…

【LeetCode刷题(数据结构与算法)】:将二叉搜索树转化为排序的双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点 特别地,我们希望可以 就地 完…

pip 时报错 no such option: --bulid-dir 的解决办法

Pycharm 安装第三方库报错及解决方案——no such option: --build-dir Pycharm 安装第三方库报错及解决方案——no such option: --build-dir 最近在学习路径规划相关内容,在运行GitHub上下载例程时缺少“plotly”库,根据网上查到的安装步骤操作&#x…

计算机组成原理 new08 电路 $\color{red}{Δ}$

文章目录 ALU基本逻辑运算复合逻辑的运算 一位全加器串行加法器串行进位的并行加法器并行进位的并行加法器(全先行进位加法器)这个明天再写。加法电路原理总结ALU和加法器有什么关系加法器原理ALU总结无符号整数/补码加减法加法器标志位的生成补码加减法发运算的溢出判断溢出电…