公司一直要求加载webview界面不能太慢,那么是什么导致webview加载的速度过慢呢?
- 渲染速度慢。
- 页面资源加载缓慢
那我们来一一想办法对应
一、渲染速度慢
前端 H5 页面渲染的速度取决于 两个方面:
- Js 解析效率
Js 本身的解析过程复杂、解析速度不快 & 前端页面涉及较多 JS 代码文件,所以叠加起来会导致 Js 解析效率非常低 - 手机硬件设备的性能
由于 Android 机型碎片化,这导致手机硬件设备的性能不可控,而大多数的Android手机硬件设备无法达到很好很好的硬件性能
解决办法:提高渲染的优先级
webView.getSettings().setRenderPriority(RenderPriority.HIGH);
2、使用webView.getSettings().setBlockNetworkImage,把图片加载放在最后来加载渲染。
本身含义阻止图片网络数据
webSettings.setBlockNetworkImage(true);
解除数据阻止
webSettings.setBlockNetworkImage(false);
示例:
webSettings.setBlockNetworkImage(true);
webView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
if(loadingProgressDialog!=null&&loadingProgressDialog.isShowing())
loadingProgressDialog.setProgress(progress);
if (progress >= 100) {
webView.getSettings().setBlockNetworkImage(false);
}
});
二、页面资源加载缓慢
这里主要提出两个解决方案
- 选择合适的 WebView 缓存
- 资源预加载
1、选择合适的 WebView 缓存
这里我要介绍一下几种常见的缓存机制
首先给出表格:
缓存机制 | 优势 | 适用场景 |
浏览器缓存机制 | HTTP协议层支持 | 静态文件的缓存 |
Dom Storage(Web Storage)存储机制 | 较大存储空间,使用简单 | 临时、简单数据的缓存,Cookies的扩展 |
Web SQL Database 存储机制 | 存储、管理复杂结构数据 | 用IndexedDB替代 不推荐 |
Application Cache 存储机制 | 方便构建离线APP | 离线APP、静态文件缓存、不推荐 |
Indexed Database 存储机制 | 存储任何类型数据、使用简单、支持索引 | 结构、关系复杂的数据存储Web SQL Datebase的替代 |
File System API | 支持文件系统的操作 | 数据适合一文件进行管理的场景,但不支持(感觉这在搞事情) |
缓存机制 优势 适用场景
浏览器缓存机制 HTTP协议层支持 静态文件的缓存
Dom Storage(Web Storage)存储机制 较大存储空间,使用简单 临时、简单数据的缓存,Cookies的扩展
存储、管理复杂结构数据 用IndexedDB替代 不推荐
Application Cache 存储机制 方便构建离线APP 离线APP、静态文件缓存、不推荐
Indexed Database 存储机制 存储任何类型数据、使用简单、支持索引 结构、关系复杂的数据存储Web SQL Datebase的替代
File System API 支持文件系统的操作 数据适合一文件进行管理的场景,但不支持(感觉这在搞事情)
浏览器缓存机制:
主要前端负责,Android 端不需要进行特别的配置。
Dom Storage(Web Storage)存储机制::
配合前端使用,使用时需要打开 DomStorage 开关。
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setDomStorageEnabled(true);
Web SQL Database 存储机制:
虽然已经不推荐使用了,但是为了兼容性,还是提供下 Android 端使用的方法
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setDatabaseEnabled(true);
final String dbPath = getApplicationContext().getDir("db",Context.MODE_PRIVATE).getPath();
webSettings.setDatabasePath(dbPath)
Application Cache 存储机制:
Application Cache(简称 AppCache)似乎是为支持 Web App 离线使用而开发的缓存机制。它的缓存机制类似于浏览器的缓存(Cache-Control 和 Last-Modified)机制,都是以文件为单位进行缓存,且文件有一定更新机制。但 AppCache 是对浏览器缓存机制的补充,不是替代。
不过根据官方文档,AppCache 已经不推荐使用了,标准也不会再支持。现在主流的浏览器都是还支持 AppCache的,以后就不太确定了。同样给出 Android 端启用 AppCache 的代码。(这里我还是建议不要用了)
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setAppCacheEnabled(true);
final String cachePath = getApplicationContext().getDir("cache",Context.MODE_PRIVATE).getPath();
webSettings.setAppCachePath(cachePath);
webSettings.setAppCacheMaxSize(5*1024*1024);
Indexed Database 存储机制:
IndexedDB 也是一种数据库的存储机制,但不同于已经不再支持的 Web SQL Database。IndexedDB 不是传统的关系数据库,可归为 NoSQL 数据库。IndexedDB 又类似于 Dom Storage 的 key-value 的存储方式,但功能更强大,且存储空间更大。
Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关就好了。(这非常好不用担心4.4以下的适配问题,我们公司要适配4.0以上还是很烦人的 O__O “…)
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
File System API:
File System API 是 H5 新加入的存储机制。它为 Web App 提供了一个虚拟的文件系统,就像 Native App 访问本地文件系统一样。由于安全性的考虑,这个虚拟文件系统有一定的限制。Web App 在虚拟的文件系统中,可以进行文件(夹)的创建、读、写、删除、遍历等操作。很可惜到目前,Android 系统的 WebView 还不支持 File System API。(就当了解一下)
2、资源的预加载
上面介绍的缓存技术,能优化二次启动 WebView 的加载速度,那首次加载 H5 页面的速度该怎么优化呢?上面分析了一次加载过程会有许多外部依赖的 JS、CSS、图片等资源需要下载,那我们能不能提前将这些资源下载好,等H5 加载时直接替换呢?答案当然是可以的!但是要牺牲一下空间,会导致APK稍微变大一点!
从 API 11(Android 3.0)开始,WebView 引入了 shouldInterceptRequest 函数,这个函数有两种重载。
public WebResourceResponse shouldInterceptRequest(WebView webView, String url) 从 API 11 引入,API 21 废弃
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) 从 API 21 开始引入
// 这里我们假设需要拦截一个图片的资源并用本地资源进行替代
mWebview.setWebViewClient(new WebViewClient() {
// 重写 WebViewClient 的 shouldInterceptRequest ()
// API 21 以下用shouldInterceptRequest(WebView view, String url)
// API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
// 下面会详细说明
// API 21 以下用shouldInterceptRequest(WebView view, String url)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
if (url.contains("logo.gif")) {
// 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif
// 图片的资源文件名为:logo.gif
InputStream is = null;
// 步骤2:创建一个输入流
try {
is =getApplicationContext().getAssets().open("images/abc.png");
// 步骤3:获得需要替换的资源(存放在assets文件夹里)
// a. 先在app/src/main下创建一个assets文件夹
// b. 在assets文件夹里再创建一个images文件夹
// c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片)
} catch (IOException e) {
e.printStackTrace();
}
// 步骤4:替换资源
WebResourceResponse response = new WebResourceResponse("image/png",
"utf-8", is);
// 参数1:http请求里该图片的Content-Type,此处图片为image/png
// 参数2:编码类型
// 参数3:存放着替换资源的输入流(上面创建的那个)
return response;
}
return super.shouldInterceptRequest(view, url);
}
// API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
// 步骤1:判断拦截资源的条件,即判断url里的图片资源的文件名
if (request.getUrl().toString().contains("logo.gif")) {
// 假设网页里该图片资源的地址为:http://abc.com/imgage/logo.gif
// 图片的资源文件名为:logo.gif
InputStream is = null;
// 步骤2:创建一个输入流
try {
is = getApplicationContext().getAssets().open("images/abc.png");
// 步骤3:获得需要替换的资源(存放在assets文件夹里)
// a. 先在app/src/main下创建一个assets文件夹
// b. 在assets文件夹里再创建一个images文件夹
// c. 在images文件夹放上需要替换的资源(此处替换的是abc.png图片
} catch (IOException e) {
e.printStackTrace();
}
// 步骤4:替换资源
WebResourceResponse response = new WebResourceResponse("image/png",
"utf-8", is);
// 参数1:http请求里该图片的Content-Type,此处图片为image/png
// 参数2:编码类型
// 参数3:存放着替换资源的输入流(上面创建的那个)
return response;
}
return super.shouldInterceptRequest(view, request);
}
});
}
看了上面代码及注释想必大家已经清楚了,就是拦截并且替换资源,从而加快网页的加载。但是同时会增加APK的大小!(强调APK瘦身的同学们注意了)