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">
目录
相关文章
|
2月前
|
存储 缓存 关系型数据库
InnoDB 引擎底层存储和缓存原理
InnoDB 引擎底层存储和缓存原理
|
2月前
|
存储 缓存 前端开发
浏览器缓存工作原理是什么?
浏览器缓存工作原理是什么?
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
3月前
|
缓存 Go API
Go 实现一个支持多种过期、淘汰机制的本地缓存的核心原理
本文旨在探讨实现一个支持多种 过期、淘汰 机制的 go 本地缓存的核心原理,我将重点讲解如何支持多样化的过期和淘汰策略。
68 0
|
25天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
53 0
|
2月前
|
缓存 Java 数据库
优化您的Spring应用程序:缓存注解的精要指南
优化您的Spring应用程序:缓存注解的精要指南
44 0
|
25天前
|
缓存 应用服务中间件 数据库
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
28 1
|
1月前
|
缓存 Java 数据库连接
mybatis 数据库缓存的原理
MyBatis 是一个流行的 Java 持久层框架,它封装了 JDBC,使数据库交互变得更简单、直观。MyBatis 支持两级缓存:一级缓存(Local Cache)和二级缓存(Global Cache),通过这两级缓存可以有效地减少数据库的访问次数,提高应用性能。
282 1
|
2月前
|
存储 缓存 算法
Golang高性能内存缓存库BigCache设计与分析
【2月更文挑战第4天】分析Golang高性能内存缓存库BigCache设计
70 0
|
3月前
|
存储 缓存 NoSQL
设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析
设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析
28 1