【源码】"拆" 网络请求库-Volley(下)

简介: Volley跟AsyncTask一样,都是老古董了,最早发布于2013年的Google I/O大会,初衷就是:让Android开发者少写重复的请求代码。

0x4、缓存设计


首先,缓存适用于那些不会更新服务端数据的请求,所以一般是缓存GET请求,很少对POST请求进行缓存。


其次,缓存是非必要的,但加了会带来两个好处:


  • 客户端/浏览器 → 减少网络延迟,加快页面打开速度;
  • 后台 → 减少带宽消耗,降低服务器压力;


在扒Volley缓存的实现细节前,我们试试自己来设计一个缓存,从一个简陋的方案开始:


  • 键值对集合存储,URL做Key,缓存(响应结果)为Value;
  • 执行请求前,先到集合里根据URL查,查到直接返回缓存,查不到执行请求,请求结束后存起来;


问题来了:后台的资源是会变的,上一秒是这个,下一秒是那个,此时客户端还用本地缓存,结果可能是不对的!


  • 后台配合,给资源加上有效期,可以是 直接过期时间,也可以是 资源生产时间 + 有效时长,客户端缓存的时候绑定上这个;
  • 客户端请求时,如果命中缓存,校验缓存是否在有效期内,在直接返回缓存,没有则执行请求;


问题又来了:后台资源变化,客户端是无感知的,以为缓存还在有效期内,不会去请求新数据。


  • 后台变化,主动通知客户端显然不太行 (硬要主动推送让客户端刷新也可以);
  • 客户端还是得主动去请求下后台;
  • 后台为资源添加上 标记,第一次请求时丢给客户端,客户端下次请求相关链接时丢给后台,后台判断TAG和自己算的是否相等,不等说明资源发生改变,给予客户端不同的反馈。比如:没发生改变,只返回一个304的状态码,发生改变,返回 200的状态码 + 新的响应,客户端根据状态码,决定是读缓存,还是解析新响应。这样还有个好处:后台的资源有可能一直没变,虽然过了有效期,但是没必要刷新,这样客户端也不需要更新缓存。


2333,上面的流程其实就是 保证HTTP缓存一致性 的部分思路,上面说到的 附加信息 都放在 信息报头 中:


网络异常,图片无法展示
|


HTTP缓存由多种规则,根据是否需要重新向服务器发起请求来分类,分为两大类:


  • 强制缓存 → 缓存数据未失效,直接使用缓存数据,用响应头中的 Expires/Cache-Control 来标明失效规则;
  • 对比缓存 → 第一次请求时后台将缓存标识一起返回给客户端,客户端再次请求数据时,将备份的缓存标识发送给服务器,服务器根据标识进行判断,返回304代表客户端可以使用缓存数据,其他则说明不能使用缓存数据。然后这个缓存标识一般分两种:资源修改时间 → Last-Modified/If-Modified-Since后台生成的资源标识 → Etag/If-None-Match后者优先级高于前者


两类缓存可以混合使用!具体的HTTP缓存策略流程图如下:


网络异常,图片无法展示
|


笔者对此也只知道个大概,感兴趣想深入了解的可见:《浏览器 HTTP 协议缓存机制详解 》


Volley中也是这样的缓存策略:


网络异常,图片无法展示
|


接着看看缓存的定义 Cache 接口,包含了一个实体缓存实体 Entity 和缓存相关的操作方法。


网络异常,图片无法展示
|


跟到具体实现类 DiskBasedCache,关注缓存增删,先看看 put()


网络异常,图片无法展示
|


网络异常,图片无法展示
|


简单说下流程:将消息报头和响应数据依次写入文件,然后存一个消息报头到缓存集合中,请求时判定缓存是否可用时用到。接着看看 get()


网络异常,图片无法展示
|


很好理解,缓存命中后,响应实体从流 → 字节数组 → Entity实例返回。其它情况返回Null,表示没有缓存可用。其它方法先不看了,接着看看这个 CacheKey 是怎么设计的?


跟下哪里调的put()方法 → NetworkDispatcher → processRequest()


网络异常,图片无法展示
|


跟下 Request → getCacheKey()


网络异常,图片无法展示
|


所以Key就是两种:


  • GET || DEPRECATED_GET_OR_POST → 直接url
  • 其它请求方式数值-url


直接URL做Key,真·简单粗暴啊!


0x5、请求处理细节


回到Volley的入口方法 newRequestQueue(),啧啧,扩展性体现之一:


网络异常,图片无法展示
|


HurlStack 对应HttpUrlConnection的封装,HttpClientStack 对应HttpClient的封装,还可以自行定制,继承 BaseHttpStack 类,重写 executeRequest() 返回处理后的响应即可。


另外,这里用 代理模式 套了一层,代理类是 BasicNetwork,在 HttpClientStack 的基础上做一些附加操作。


网络异常,图片无法展示
|


网络异常,图片无法展示
|


所以,这个附加操作就是:获取缓存相关的请求头,对响应结果/异常的处理。然后,看到异常重试都是调用的 attemptRetryOnException()


网络异常,图片无法展示
|


跟下 RetryPolicy 发现是一个接口:


网络异常,图片无法展示
|


看下哪里实现了这个接口,跟到默认实现类 DefaultRetryPolicy,可以看到默认最大重试次数为1:


网络异常,图片无法展示
|


调用一次 retry() 计数加1,如果<=最大重试次数,抛出异常


网络异常,图片无法展示
|


可以看到这里只是做了一个计数,并没有进行执行请求,或者请求入队的操作,那请求是怎么重新发起的?


网络异常,图片无法展示
|


注意这里的死循环,以及 重试方法的层级,是在catch里的,如果此时超过重试次数,抛出异常,就能退出这个死循环。


这样不会崩溃吗?当然不会,因为在调用此方法的 NetworkDispatcher 中套了一层try-catch兜底:


网络异常,图片无法展示
|


当真是妙啊!!!


最后还有一个点:Volley使用 内存缓存 来存放请求获得的数据,而不是直接在内存中开辟一个区域存放。


这是为啥?因为网络请求一般会很频繁,不停创建byte[],会引起频繁的GC,间接对APP性能造成影响。所以Volley定义了 ByteArrayPool 来缓存数据。


网络异常,图片无法展示
|


看着有点懵是吧,我简单说说:


  • 规定了缓存池的默认大小为:DEFAULT_POOL_SIZE = 4096字节 = 4KB,池中维护两个列表,一个按照 最近使用顺序 排序(表①),一个按照 byte[]大小 排序(表②);
  • 从缓存区取空间:不直接开辟空间,先循环迭代查找列表②中是否有 len>=所需字节 的已开辟空间,有的话返回此空间,没有的话开辟新的空间返回;
  • 将空间返还给缓存区:检查插入数据是否超出边界,有的话直接返回,没有的话添加到表①尾部,然后二分查找找到表②中合适的位置插入,当前开辟字节数增加。同时判断是否超过最大值,是回收表②的第一个元素;


0x6、可扩展性


Tips:其实看下都有哪些接口和抽象类,就知道有啥可以自定义的~


RequestQueue构造方法传入


  • Cache缓存,默认是将响应数据放磁盘中,你可以弄成二三级缓存,实现接口;
  • Network请求框架封装,默认封了HttpUrlConnection和HttpClient,弄成其他请求库也可以;
  • ResponseDelivery响应交付,就是请求响应的后续处理,正常与异常情况的处理;


Request构造方法传入


  • RetryPolicy → 请求失败重试策略;


其它


  • Request请求,toolbox里有常用的实现类,如:JsonRequest、ImageRequest、StringRequest等;
  • Authenticator令牌授权


大概就这些吧,toolbox里还送了一个图片加载的工具,不过性能不算特别好,就不展开讲了。


0x7、小结


本节对请求库Volley的设计进行了多方面的拆解,获益良多,至少现在让我封装一个请求库,不会无从入手了。


源码难啃,但弄懂了设计的原理,就会觉得豁然开朗,妙啊,也顺应了前老大说的,品经典项目源码,如品经典名著般,沁人心脾~


参考文献:



相关文章
|
2月前
|
机器学习/深度学习
NeurIPS 2024:标签噪声下图神经网络有了首个综合基准库,还开源
NoisyGL是首个针对标签噪声下图神经网络(GLN)的综合基准库,由浙江大学和阿里巴巴集团的研究人员开发。该基准库旨在解决现有GLN研究中因数据集选择、划分及预处理技术差异导致的缺乏统一标准问题,提供了一个公平、用户友好的平台,支持多维分析,有助于深入理解GLN方法在处理标签噪声时的表现。通过17种代表性方法在8个常用数据集上的广泛实验,NoisyGL揭示了多个关键发现,推动了GLN领域的进步。尽管如此,NoisyGL目前主要适用于同质图,对异质图的支持有限。
57 7
|
4月前
|
JavaScript 前端开发 API
网络请求库 – axios库
网络请求库 – axios库
223 60
|
4月前
|
数据采集 JSON API
🎓Python网络请求新手指南:requests库带你轻松玩转HTTP协议
本文介绍Python网络编程中不可或缺的HTTP协议基础,并以requests库为例,详细讲解如何执行GET与POST请求、处理响应及自定义请求头等操作。通过简洁易懂的代码示例,帮助初学者快速掌握网络爬虫与API开发所需的关键技能。无论是安装配置还是会话管理,requests库均提供了强大而直观的接口,助力读者轻松应对各类网络编程任务。
149 3
|
4月前
|
机器学习/深度学习 JSON API
HTTP协议实战演练场:Python requests库助你成为网络数据抓取大师
在数据驱动的时代,网络数据抓取对于数据分析、机器学习等至关重要。HTTP协议作为互联网通信的基石,其重要性不言而喻。Python的`requests`库凭借简洁的API和强大的功能,成为网络数据抓取的利器。本文将通过实战演练展示如何使用`requests`库进行数据抓取,包括发送GET/POST请求、处理JSON响应及添加自定义请求头等。首先,请确保已安装`requests`库,可通过`pip install requests`进行安装。接下来,我们将逐一介绍如何利用`requests`库探索网络世界,助你成为数据抓取大师。在实践过程中,务必遵守相关法律法规和网站使用条款,做到技术与道德并重。
79 2
|
4月前
|
数据采集 网络协议 API
HTTP协议大揭秘!Python requests库实战,让网络请求变得简单高效
【9月更文挑战第13天】在数字化时代,互联网成为信息传输的核心平台,HTTP协议作为基石,定义了客户端与服务器间的数据传输规则。直接处理HTTP请求复杂繁琐,但Python的`requests`库提供了一个简洁强大的接口,简化了这一过程。HTTP协议采用请求与响应模式,无状态且结构化设计,使其能灵活处理各种数据交换。
99 8
|
4月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
91 3
|
4月前
|
JSON API 开发者
Python网络编程新纪元:urllib与requests库,让你的HTTP请求无所不能
【9月更文挑战第9天】随着互联网的发展,网络编程成为现代软件开发的关键部分。Python凭借简洁、易读及强大的特性,在该领域展现出独特魅力。本文介绍了Python标准库中的`urllib`和第三方库`requests`在处理HTTP请求方面的优势。`urllib`虽API底层但功能全面,适用于深入控制HTTP请求;而`requests`则以简洁的API和人性化设计著称,使HTTP请求变得简单高效。两者互补共存,共同推动Python网络编程进入全新纪元,无论初学者还是资深开发者都能从中受益。
67 7
WK
|
4月前
|
数据采集 XML 安全
常用的Python网络爬虫库有哪些?
Python网络爬虫库种类丰富,各具特色。`requests` 和 `urllib` 简化了 HTTP 请求,`urllib3` 提供了线程安全的连接池,`httplib2` 则具备全面的客户端接口。异步库 `aiohttp` 可大幅提升数据抓取效率。
WK
144 1
|
4月前
|
网络协议 Linux C++
超级好用的C++实用库之网络
超级好用的C++实用库之网络
77 0