一、背景目前的App开发或多或少都采用了Hybrid模式。在WebView中,经常会加载一些js文件(比如用于与WebView进行Native通信的bridge.js),而这些js文件不会经常变化,所以我们希望在WebView加载一次js后,如果有js没有变化,下次不需要再发起网络请求加载,从而减少流量和资源占用。那么有什么办法可以达到这个目的呢?首先从WebView的缓存原理说起。2、WebView的缓存类型WebView主要包括两种缓存,一种是浏览器自带的网页数据缓存,所有浏览器都支持,由HTTP协议定义;另一个是H5缓存,由网页开发者设置。H5缓存主要包括AppCache、DOMStorage、LocalStorage、WebSQLDatabase存储机制等,这里主要介绍AppCache来缓存js文件。三、浏览器自身的网页数据缓存1、工作原理浏览器缓存机制是一种通过HTTP协议Header中的Cache-Control(或Expires)、Last-Modified(或Etag)等字段来控制文件缓存的机制。关于这些字段的作用和浏览器的缓存更新机制,可以看这两篇文章(H5缓存机制浅析,移动端Web加载性能优化,Android:手把手教你搭建WebView缓存机制&资源预加载方案),其中包含详细介绍。下面从我实际应用的角度,介绍一下HTTP协议中经常遇到的headers。这两个字段是浏览器在收到响应时决定文件是否需要缓存的字段,或者当文件需要加载时浏览器是否需要发出请求的字段。Cache-Control:max-age=315360000,表示缓存时长为315360000秒。如果在315360000秒内需要再次请求该文件,浏览器将不再请求,直接使用本地缓存的文件。这是HTTP/1.1标准中的一个字段。Expires:Thu,31Dec203723:55:55GMT,也就是说这个文件的过期时间是2037年12月31日晚上23:55:55。在这个时间之前,浏览器不会再发送请求到获取此文件。这是HTTP/1.0中的一个字段。如果客户端和服务器的时间不同步,就会导致缓存出现问题。于是就有了上面的Cache-Control。当它们同时出现在HTTPResponseHeader中时,Cache-Control的优先级更高。下面两个字段是服务器发起请求时用来判断文件是否需要更新的字段。Last-Modified:Wed,28Sep201609:24:35GMT,表示该文件的最后修改时间为2016年9月28日9:24:35。对于浏览器,该字段将被包含为If-Modified-Since字段在下一个请求中的RequestHeader。比如浏览器缓存的文件已经超过了Cache-Control(或者Expires),那么当需要加载文件的时候,就会发送一个请求,请求的header中有一个字段为If-Modified-Since:2016年9月28日星期三09:24:35GMT。服务器收到请求后,会将文件的Last-Modified时间与这个时间进行比较。如果时间没有改变,浏览器会返回304NotModified给浏览器,并且content-length必须是0bytes。如果时间发生变化,服务器会返回200OK,并将相应的内容返回给浏览器。ETag:“57eb8c5c-129”,这是文件的特征字符串。功能与上面的Last-Modified相同。只有当浏览器发出下一个请求时,ETag才会作为RequestHeader中的If-None-Match:"57eb8c5c-129"字段传递给服务器。比较服务器和***的文件特征串,相同则返回304NotModified,不同则返回200OK。当ETag和Last-Modified同时出现时,只要任何一个字段生效,就认为文件没有更新。2、如何设置WebView支持以上协议从上面的介绍来看,只要是主流的、合格的浏览器,在HTTP协议层面应该都可以支持这些字段。这不是我们开发者可以修改的配置,也不应该修改。在Android上,我们的WebView也支持这些字段。但是我们可以通过代码设置WebView的CacheMode来使协议有效或者无效。WebView有以下几种缓存模式:LOAD_CACHE_ONLY:不使用网络,只读取本地缓存数据。LOAD_DEFAULT:根据cache-control决定是否从网络取数据。LOAD_CACHE_NORMAL:在API级别17中已过时,它与API级别11中的LOAD_DEFAULT模式相同LOAD_NO_CACHE:不使用缓存,仅从网络获取数据。LOAD_CACHE_ELSE_NETWORK,只要本地有,不管是过期还是无缓存,都会使用缓存中的数据。仅当没有本地缓存??时才从网络中获取。设置WebView缓存CacheMode的示例代码如下:WebSettingssettings=webView.getSettings();settings.setCacheMode(WebSettings.LOAD_DEFAULT);网上很多人说根据网络情况选择CacheMode。当有网络时,将其设置为LOAD_DEFAULT。没有网络时设置为LOAD_CACHE_ELSE_NETWORK。但是在我的业务中,js文件的更新都是非覆盖更新,也就是js文件每次变化,文件的url地址肯定会变化,所以希望浏览器能够缓存js,保存到使用它,然后我将它设置为LOAD_CACHE_ELSE_NETWORK。当然,如果你能改一下js的cdn服务器的Cache-Control字段,那也是可以的。只需使用LOAD_DEFAULT。至于文件更新是覆盖还是非覆盖,不是我今天要讨论的。在web前端领域,这是一个可以聊的话题。关于iOS的WebView,同事在实际测试中发现,控制文件缓存的ResponseHeader就是Expires字段。.而且iOS无法为整个WebView设置CacheMode,只能为每个URLRequest设置。.以后有机会去了解一下iOS的情况。3、手机中的存储路径浏览器缓存的文件默认是怎么存储的?这个问题从接触WebView开始就一直是个谜。这次由于工作需要,特意root了两台手机,一台红米1(安卓4.4)和一台小米4c(安卓5.1),两台高root系统版本(6.0和7.1)的Nexus都root了with失败后,我决定看一下WebView自带的缓存在4.4和5.1系统上存放在哪里。首先,不用想也能知道,这些文件一定在/data/数据/包名/目录下。在我之前的博客中提到过,这是每个应用程序的内部存储目录。接下来,我们打开终端,使用adb连接手机,然后按照下面的命令操作。//1。首先进入shelladbshel??l//2。打开root账号su//3。修改文件夹权限chmod777data/data/你的应用包名///4.修改子文件夹权限,因为Android命令行不支持Recursivechmod的实现类似于Linux中的-R命令。..chmod777data/data/你的应用包名/*//5.所以如果你有更深层次的应用目录,你需要进一步chmod。..chmod777data/data/你的应用包名/*/*//6.直到终端提示你说nosuchfileordirectory,就说明chmod结束了,可以看到内部存储中的所有文件夹和文件了。有的话请告诉我好的方法,谢谢~Android4.4目录:/data/data/包名/app_webview/cache/,第二个文件夹如下图。您可能已经注意到第一个文件夹名为ApplicationCache,我们稍后会讲到。Android5.1的目录:/data/data/包名/cache/org.chromium.android_webview/下,如下图。但是在5.1系统上,/data/data/包名/app_webview/文件夹依然存在,但是4.4系统上存放WebView自身缓存的app_webview/cache文件夹已经不存在了(注意AppCache目录还在),如下所示。综上所述,WebView自带的浏览器协议支持的缓存在不同的系统版本上位置不同。可能除了我已经root过的4.4和5.1,其他版本系统的WebView内置缓存也可能存在于不同的目录下。还有一个是关于缓存文件的存储格式和索引格式,在不同的手机上可能不一样,因为之前在网上看到有人说有一个文件叫webview.db或者webviewCache.db,这个文件是不在app_webview/cache或org.chromium.android_webview中,而是在/data/data/packagename/database/中。但是我的两台root过的手机都没有看到这种文件,我打开/data/data/包名/下的所有db文件,也没有发现存放url记录的表。.其实以5.1系统为例,我看到/data/data/包名/cache/org.chromium.android_webview/下有index和/index-dir/the-real-index的文件,还有一个bunchofnames是一个md5+underscore+number的文件,在上图中也可以看到。对这块的原理还有一些疑惑,希望有专业的高手解答。4、H5的缓存说完了WebView自带的缓存,再来说说H5中的AppCache。这个Cache是??由开发网页的开发者控制的,不是Native控制的,但是Native中的WebView也需要我们做一些设置来支持H5的这个特性。一、工作原理在编写网页代码时,指定manifest属性,使页面使用AppCache。通常html页面代码会这样写:
