目录
- 引言:Html加载流程
- 加载流程各节点耗时分析优化
- 加载流程结构优化
- 客户端优化
Html加载流程
- 创建并初始化WebView
- 下载网页所需资源文件
- 渲染展示网页
加载流程各节点耗时分析优化
-
WebView创建初始化
首次初始化WebView会比第二次初始化慢很多。初始化后,即使WebView已释放,但一些多WebView共用的全局服务/资源对想仍未释放,而第二次初始化不需要生成,因此初始化变快。1. 提前初始化, 提前预备全局WebView,Application中初始化WebView备用 @Override public void onCreate() { // Application中提前初始化WebView WebView mWebView = new WebView(new MutableContextWrapper(this)) }
注:该方式会增加冷启动时间
2. WebView复用,维护WebView pool,避免每次打开网页创建WebView。
例如:VasSonic方案
-
DNS解析耗时
html网页文件获取过程中,首先需要将url链接解析成ip地址,根据ip地址获取到对应html文件。1. DNS会在系统级别进行缓存,对于WebView的地址,如果使用的域名与原生api的相同,则可直接使用缓存,避免DNS解析耗时。
-
资源文件下载耗时
弱网情况下,下载网页资源耗时,导致白屏时间过长。1. 网页资源压缩,并CDN加速处理,缩短请求耗时。 2. 服务端下发填充好首屏数据的网页,作为网页首屏展示,减少网页上数据请求时间。 3. App闲置状态时,下载离线包到本地,加载时优先加载离线包数据。后续更新接收下发的差异包,与当前离线包合并成完整包。
HTML解析耗时
解析html文件,构建DOM树,耗时取决于html节点嵌套复杂程度,与硬件解析能力强弱。-
css文件加载解析、js加载
一般来说Html在开始接收到数据返回数据时就开始解析并构建DOM树,如果没有JS阻塞的话一般会相继完成,通常情况下,下面代码的link部分和script部分如果单独出现,都不会阻塞页面的解析,但,当两部分同时出现时,css加载会阻塞下面的内联JS的执行,从而阻塞阻塞Html文件的解析。1. 为防止JS阻塞Html的解析,Web端需延迟JS解析。
-
绘制渲染
布局绘制是一个递归过程,从呈现根节点开始,递归遍历子节点,计算几何集合信息。因此,html标签越复杂、嵌套越深,则布局耗时越久。1. 优化网页布局,减少布局层次。
加载流程结构优化
- 流程优化
默认状态下,WebView初始化与资源文件下载为线性同步执行,此时WebView初始化时,网络为空闲状态,并行处理WebView初始化与资源文件下载可缩短网页显示的总耗时。第一阶段为:创建初始化WebView(Launch WebView)
第二阶段为:请求网页资源(Native(Sonic) Request)
但,由于WebView初始化与请求网页资源操作结束时间先后无可得知。
方案:流式拦截
,加入中间层来桥接内核和数据
- 启动子线程请求页面主资源,子线程中不断将网络数据读取到内存中。
- WebView初始化完成的时候,提供中间层BridgeStream来连接WebView和数据流;
- WebView读取数据时,中间层BridgeStream先把内存的数据读取返回后,再继续读取网络的数据。
通过桥接流的方式,整个内核无需等待,边加载边解析
客户端优化
-
合理使用WebView提供的几种缓存方式
1. 浏览器缓存。内置自动实现
2. Application Cache 缓存webSettings.setAppCacheEnabled(true)
webSettings.setAppCacheMaxSize(yourCacheSize)
webSettings.setAppCachePath(yourCacheDirPath)- DOM Storage 缓存
setDomStorageEnabled(true)
- Web SQL Database 缓存
webSettings.setDatabaseEnabled(true)
webSettings.setDatabasePath(yourCacheDirPath)- Indexed Database 缓存。Android 4.4以上支持
webSettings.setJavaScriptEnabled(true)
如何缓存html、js、css、图片等文件,通过缓存机制,对于应用提高资源文件的加载速度、流量优化有很大意义。而具体该针对哪种资源使用哪个缓存字段,以及缓存时长设置就比较重要。若时长设置的太短,则缓存效果受影响,若时长设置过长,则不能及时获取服务器最新数据。
html、js、css资源
考虑到这些文件会随着业务需求经常变化。为此,这些文件可以使用Last-Modified(Etag)
来控制缓存图片、音视频资源
图片也可以通过Last-Modified(Etag)
来控制缓存。但这样每次都需向服务器发起查询请求,考虑到图片文件长时间变动不大,推荐使用Cache-Control
设置一个较长的时间来缓存。除此之外,也可以使用
Application Cache
机制,由前端控制缓存文件,客户端设置缓存路径和大小。
由于,WebView各缓存机制的缓存大小有限,时常导致最开始的缓存被清理;浏览器缓存Last-Modied
和ETag
控制不完全,依然无法很好的避免过渡请求网页资源,因此,需要一套可控的缓存策略,来突破缓存容量过小和图片缓存策略统一的问题。
-
资源本地化
网页加载到显示,需要下载大量文件,其中不乏有些体积大并且基本固定不动的资源,此时,可把这些资源预置在项目中,加载时从本地加载,方案与离线包
方案类似。
大致操作如下:
- 方式一、JS方法注入
// 1. 固定资源放到项目文件夹,如/Assets文件夹中
// 2. 注入JS方法
webView.addJavascriptInterface(new JsInterface(), "JsInterface")
private class JsInterface {
@JavascriptInterface
public String getLocalSrc(String src) {
return "file://storage/emulated/0/app/file/a.jpeg"
}
}
// 3. 页面加载完成时,修改图片标签
private class MyWebViewClient extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String js = "javascript:(function() {
var objs = document.getElementsByTagName('img');
for (var i = 0; i < objs.length; i++) {
var imgUrl = objs[i].getAttribute('src');
var localUrl = window.local_obj.getLocalSrc(imgUrl);
if (localUrl) {
objs[i].setAttribute('src', localUrl);
}
}
})()"
view.loadUrl(js);
}
}
- 方式二、请求拦截
webView.setWebViewClient(new WebViewClient() {
// 为方便,此处进写Api 21以下方法,Api21 以上雷同
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// 1. 判断拦截资源的条件
if (url.contains("logo.gif")) {
// 假设网页图片资源为:http://abc.com/image/logo.gif
// 图片资源文件名为:logo.gif
// 2. 创建输入流
InputStream is = null
try {
// 3. 获得需要替换的资源(存放在assets文件夹中)
is = getApplicationContext().getAssets().open("image/abc.png")
} catch (IOException e) {
e.printStackTrace()
}
// 4. 替换资源
WebResourceResponse reponse = new WebResourceResponse("image/png", "utf-8", is)
return response
}
return super.shouldInterceptRequest(view, url)
}
})
此外,客户端需要预置资源,并维护网络图片url和本地图片自检的关联,根据一定策略保证本地预置资源为最新资源。若预置资源不限于图片资源,由于html、js、css等资源容易发生变化,因此还需实现一套机制实现本地资源和服务端数据及时的更新。即服务端需要支持版本控制和资源增量下发等功能。
总结
结合手Q、网易严选、美团等其他方面优化方案,大致如下:
- WebView在Application中提前初始化
- 实现WebView复用
- 另开WebView进程
- DNS解析优化(接口与网页主域名一致)
- 线上资源压缩、CDN加速
- 静态直出,直接下发首屏html
- 离线预推,下发离线包,并增量更新
- WebView创建与网络请求并行
- 网页按节点局部刷新
- 自定义实现图片资源缓存
- 重新定义图片加载格式,
shareP
- 本地资源拦截替换