做混合开发的朋友应该都遇到过这种场景:H5页面改了接口返回值,安卓端刷新一下就生效,iOS却还显示旧数据;或者App里嵌的网页突然卡顿、白屏,清完App缓存才恢复正常。问题十有八九出在WebView的缓存机制上——它不像浏览器那么“透明”,不同系统底层策略差异大,一不留神就踩坑。
安卓WebView缓存怎么工作的?
安卓WebView默认开启多种缓存:HTTP缓存(基于Cache-Control/Expires)、DNS预解析、资源文件缓存(JS/CSS/图片),甚至还有数据库缓存(比如IndexedDB)。关键控制点在WebSettings:
WebSettings settings = webView.getSettings();
settings.setCacheMode(WebSettings.LOAD_DEFAULT); // 默认行为
// 可选值还有:
// LOAD_CACHE_ONLY(只读缓存)
// LOAD_NO_CACHE(完全禁用)
// LOAD_CACHE_ELSE_NETWORK(先试缓存,失败再联网)特别注意:LOAD_CACHE_ELSE_NETWORK在弱网下容易导致页面“假死”——它会等缓存超时才发网络请求,而超时时间可能长达几分钟。线上App建议慎用。
iOS WKWebView缓存更“倔强”
iOS从WKWebView开始,缓存逻辑交给NSURLCache统一管理,默认内存+磁盘双层缓存,且对Cache-Control: max-age=3600这类头极其敏感。哪怕H5加了<meta http-equiv="Cache-Control" content="no-cache">,也挡不住原生层的缓存拦截。
常见翻车现场:
• 用location.reload()在iOS上无效,因为WKWebView会直接走缓存响应;
• 接口返回JSON被缓存,改了后端字段,前端收不到新结构;
• 静态资源(如app.js?v=1.2)没带版本号,改了代码用户还是跑老包。
实战解决方案
安卓端快速清理缓存:
webView.clearCache(true); // 清理磁盘缓存
webView.clearHistory();
CookieManager.getInstance().removeAllCookies(null);iOS端绕过缓存(推荐):
在发起请求前,给URL加随机参数或时间戳,比如:https://api.example.com/data?_t=1712345678901;
或者在WKNavigationDelegate中拦截请求,强制设置缓存策略:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let response = navigationResponse.response as? HTTPURLResponse,
let url = response.url,
url.host?.contains("your-api-domain") == true {
decisionHandler(.allow)
return
}
decisionHandler(.allow)
}更彻底的办法:全局禁用API缓存(仅限业务接口),在NSURLSessionConfiguration里设:configuration.requestCachePolicy = .reloadIgnoringLocalCacheData。
日常调试小技巧
• 安卓:打开Chrome DevTools(chrome://inspect),连上设备,看Network面板里的Size列——如果显示from disk cache或from memory cache,说明缓存生效了;
• iOS:用Mac上的Console App过滤WKWebView日志,搜索cache关键词;
• 统一建议:H5项目上线前,静态资源务必加哈希后缀(如index.a1b2c3.js),接口URL加版本号或时间戳参数,别依赖“用户手动下拉刷新”来破缓存。