WebView 缓存原理分析和应用

简介:

一、背景

现在的App开发,或多或少都会用到Hybrid模式,到了WebView这边,经常会加载一些js文件(例如和WebView用来Native通信的bridge.js),而这些js文件不会经常发生变化,所以我们希望js在WebView里面加载一次之后,如果js没有发生变化,下次就不用再发起网络请求去加载,从而减少流量和资源的占用。那么有什么方式可以达到这个目的呢?先得从WebView的缓存原理入手。
二、WebView的缓存类型
WebView主要包括两类缓存,一类是浏览器自带的网页数据缓存,这是所有的浏览器都支持的、由HTTP协议定义的缓存;另一类是H5缓存,这是由web页面的开发者设置的,H5缓存主要包括了App Cache、DOM Storage、Local Storage、Web SQL Database 存储机制等,这里我们主要介绍App Cache来缓存js文件。
三、浏览器自带的网页数据缓存
1.工作原理
浏览器缓存机制是通过HTTP协议Header里的Cache-Control(或Expires)和Last-Modified(或 Etag)等字段来控制文件缓存的机制。关于这几个字段的作用和浏览器的缓存更新机制,大家可以看看这两篇文章(H5 缓存机制浅析 移动端 Web 加载性能优化,Android:手把手教你构建 WebView 的缓存机制 & 资源预加载方案),里面有详细的介绍。下面从我实际应用的角度,介绍一下通常会在HTTP协议中遇到的Header。
这两个字段是接收响应时,浏览器决定文件是否需要被缓存;或者需要加载文件时,浏览器决定是否需要发出请求的字段。
Cache-Control:max-age=315360000,这表示缓存时长为315360000秒。如果315360000秒内需要再次请求这个文件,那么浏览器不会发出请求,直接使用本地的缓存的文件。这是HTTP/1.1标准中的字段。
Expires: Thu, 31 Dec 2037 23:55:55 GMT,这表示这个文件的过期时间是2037年12月31日晚上23点55分55秒,在这个时间之前浏览器都不会再次发出请求去获取这个文件。这是HTTP/1.0中的字段,如果客户端和服务器时间不同步会导致缓存出现问题,因此才有了上面的Cache-Control,当它们同时出现在HTTP Response的Header中时,Cache-Control优先级更高。
下面两个字段是发起请求时,服务器决定文件是否需要更新的字段。
Last-Modified:Wed, 28 Sep 2016 09:24:35 GMT,这表示这个文件最后的修改时间是2016年9月28日9点24分35秒。这个字段对于浏览器来说,会在下次请求的时候,作为Request Header的If-Modified-Since字段带上。例如浏览器缓存的文件已经超过了Cache-Control(或者Expires),那么需要加载这个文件时,就会发出请求,请求的Header有一个字段为If-Modified-Since:Wed, 28 Sep 2016 09:24:35 GMT,服务器接收到请求后,会把文件的Last-Modified时间和这个时间对比,如果时间没变,那么浏览器将返回304 Not Modified给浏览器,且content-length肯定是0个字节。如果时间有变化,那么服务器会返回200 OK,并返回相应的内容给浏览器。
ETag:”57eb8c5c-129”,这是文件的特征串。功能同上面的Last-Modified是一样的。只是在浏览器下次请求时,ETag是作为Request Header中的If-None-Match:"57eb8c5c-129"字段传到服务器。服务器和最新的文件特征串对比,如果相同那么返回304 Not Modified,不同则返回200 OK。当ETag和Last-Modified同时出现时,任何一个字段只要生效了,就认为文件是没有更新的。
2.WebView如何设置才能支持上面的协议
由上面的介绍可知,只要是个主流的、合格的浏览器,都应该能够支持HTTP协议层面的这几个字段。这不是我们开发者可以修改的,也不是我们应该修改的配置。在Android上,我们的WebView也支持这几个字段。但是我们可以通过代码去设置WebView的Cache Mode,而使得协议生效或者无效。WebView有下面几个Cache Mode:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据。
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃,从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据。
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。本地没有缓存时才从网络上获取。
设置WebView缓存的Cache Mode示例代码如下:
WebSettings settings = webView.getSettings();
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
网上很多人都说根据网络条件去选择Cache Mode,当有网络时,设置为LOAD_DEFAULT,当没有网络时设置为LOAD_CACHE_ELSE_NETWORK。但是在我的业务中,js文件的更新都是非覆盖式的更新,也就是时候每次改变js文件的时候,文件的url地址一定会发生变化,所以我希望浏览器能够缓存下来js,并且一直使用它,那么我就给它只设置为LOAD_CACHE_ELSE_NETWORK。当然如果你要是可以改js的cdn服务器的Cache-Control字段,那也行啊,用LOAD_DEFAULT就ok了。至于文件是应该采用覆盖式or非覆盖式的更新,不是我今天要讨论的内容,在web前端领域,这是一个可以聊聊的topic。
关于iOS的WebView,我同事在实际测试的时候竟然发现,控制文件缓存的Response Header是Expires字段。。而且iOS无法针对整个WebView设置Cache Mode,只能针对每一个URLRequest去设置。。后续有机会要学习一下iOS那块的情况。
3.在手机里面的存储路径
浏览器默认缓存下来的文件是怎么被存储到了哪里呢?这个问题在接触到WebView以来,就一直是一个谜题。这次由于工作的需要,我特意root了两台手机,一台红米1(Android 4.4)和一台小米4c(Android 5.1),在root高系统版本(6.0和7.1)的两台Nexus都以失败告终之后,我决定还是先看看4.4和5.1系统上,WebView自带的缓存存到了哪里。
首先,不用思考就知道,这些文件一定是在/data/data/包名/目录下,在我之前的一篇博客里面提到过,这是每一个应用自己的内部存储目录。
接着,我们打开终端,使用adb连接手机,然后按照下面命令操作一下。
// 1.先进入shell
adb shell
// 2.开启root账号
su
// 3.修改文件夹权限
chmod 777 data/data/你的应用包名/
// 4.修改子文件夹的权限,因为Android命令行不支持向Linux那样的-R命令实现递归式的chmod。。。
chmod 777 data/data/你的应用包名/*
// 5.所以如果你对应用目录层级更深,你就要进一步地chmod。。。
chmod 777 data/data/你的应用包名//
// 6.直到终端里提示你说,no such file or directory时,说明chmod完了,所有的内部存储里面的文件夹和文件都可以看到了,如果大家有更好的方法请一定告诉我,多谢了~
Android 4.4的目录:/data/data/包名/app_webview/cache/,如下图所示的第二个文件夹。

可能你注意到了,第一个文件夹是叫Application Cache,我们后面再说它。
Android 5.1的目录:/data/data/包名/cache/org.chromium.android_webview/下面,如下图所示。

但是在5.1系统上,/data/data/包名/app_webview/文件夹依然存在,只是4.4系统上面存储WebView自带缓存的app_webview/cache文件夹不再存在了(注意下App Cache目录还在),如下图所示。

综上所述,WebView自带的浏览器协议支持的缓存,在不同的系统版本上,位置是不一样的。也许除了我root过的4.4、5.1以外,其他版本系统的WebView自带缓存还可能存在于不同的目录里面。
另外一个是关于缓存文件的存储格式和索引格式,在不同的手机上可能也有差别,因为之前看到网上的人都说有叫webview.db或者webviewCache.db的文件,这个文件呢,还不是在app_webview/cache或者org.chromium.android_webview下面,而是在/data/data/包名/database/里面。但是,我这两台root过的手机都没有看到这种文件,而且我把/data/data/包名/下面所有的db文件都打开看了,并没有发现有存储url记录的table。。
实际上,以5.1系统为例,我看到了/data/data/包名/cache/org.chromium.android_webview/下面有叫index和/index-dir/the-real-index的文件,以及一堆名称为md5+下划线+数字的文件,上面的图中也可以看得到,这块的原理仍然有些疑问,也希望专业的大神可以解答一下。
四、H5的缓存
讲完了WebView自带的缓存,下面讲一下H5里面的App Cache。这个Cache是由开发Web页面的开发者控制的,而不是由Native去控制的,但是Native里面的WebView也需要我们做一下设置才能支持H5的这个特性。
1.工作原理
写Web页面代码时,指定manifest属性即可让页面使用App Cache。通常html页面代码会这么写:

manifest="xxx.appcache">
目录
相关文章
|
23天前
|
缓存 NoSQL Java
Redis深度解析:解锁高性能缓存的终极武器,让你的应用飞起来
【8月更文挑战第29天】本文从基本概念入手,通过实战示例、原理解析和高级使用技巧,全面讲解Redis这一高性能键值对数据库。Redis基于内存存储,支持多种数据结构,如字符串、列表和哈希表等,常用于数据库、缓存及消息队列。文中详细介绍了如何在Spring Boot项目中集成Redis,并展示了其工作原理、缓存实现方法及高级特性,如事务、发布/订阅、Lua脚本和集群等,帮助读者从入门到精通Redis,大幅提升应用性能与可扩展性。
49 0
|
8天前
|
缓存 JavaScript 中间件
优化Express.js应用程序性能:缓存策略、请求压缩和路由匹配
在开发Express.js应用时,采用合理的缓存策略、请求压缩及优化路由匹配可大幅提升性能。本文介绍如何利用`express.static`实现缓存、`compression`中间件压缩响应数据,并通过精确匹配、模块化路由及参数化路由提高路由处理效率,从而打造高效应用。
29 6
|
16天前
|
小程序 Android开发 iOS开发
ISO钉钉小程序小程序webview打开nextjs应用异常
ISO钉钉小程序小程序webview打开nextjs应用异常
34 3
|
18天前
|
存储 缓存 前端开发
缓存技术在软件开发中的应用与优化策略
缓存技术在软件开发中的应用与优化策略
|
26天前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
56 1
|
20天前
|
开发工具 Android开发 iOS开发
从零开始学 Xamarin 开发,新手教程全攻略,安装环境、创建项目、设计界面,轻松开启开发之旅!
【8月更文挑战第31天】Xamarin是一种高效的跨平台移动应用开发工具,迎合了日益增长的移动应用需求。本文为Xamarin新手提供了一套详尽的入门指南,涵盖开发环境搭建、项目创建与配置、用户界面设计及功能实现等关键步骤。通过具体示例,帮助初学者快速上手Xamarin开发,开启移动应用创作之旅。
26 0
|
21天前
|
缓存 数据库 UED
【性能翻倍的秘密】Rails高手从不告诉你的缓存技巧,竟让应用瞬间提速,背后真相令人震惊!
【8月更文挑战第31天】缓存是提升Web应用性能的关键技术,Ruby on Rails内置了多种缓存机制,如页面缓存、动作缓存、片段缓存及数据库查询缓存,可显著减少数据库查询次数并加快页面加载速度。本文通过具体案例分析了这些缓存策略,并提供了示例代码,展示如何在实际应用中实施这些策略,帮助开发者提升用户体验。
36 0
|
25天前
|
存储 缓存 NoSQL
微服务缓存原理与最佳实践
微服务缓存原理与最佳实践
|
26天前
|
缓存 NoSQL Linux
【Azure Redis 缓存】应用中出现连接Redis服务错误(production.ERROR: Connection refused)的排查步骤
【Azure Redis 缓存】应用中出现连接Redis服务错误(production.ERROR: Connection refused)的排查步骤
|
27天前
|
缓存 NoSQL 网络协议
【Azure Redis 缓存】Azure Redis Cluster 在增加分片数时失败分析
【Azure Redis 缓存】Azure Redis Cluster 在增加分片数时失败分析