提速
一个网页的加载过程总体的几个步骤
| 阶段 | 耗时 | 说明 |
|---|---|---|
| webview 初始化 | 约100ms~250ms | 首次加载 webview 内核耗时 |
| html 解析 | 约300ms~500ms | Performance 里面 Finish Loading 的值是 0 ,可能这段时间包含了下载的耗时 |
| 静态资源下载 | 约50~200ms | 此为同步资源消耗,异步资源未计 |
WebViewPool - 打造 WebView 缓存池,用于抵消启动浏览器内核的耗时
由于WebView初始化需要消耗一定的时间,并且不同设备之间该耗时时间略有差异,在配置较低的设备上耗时较长,为了优化这部分耗时,我们定义了全局 WebView 对象并提前初始化。
技术点一:缓存池的设计
自定义一个访问优先的 WebView 缓存池,默认大小为3。访问缓存池都会从末尾节点向前遍历,如果找到 WebView ,就直接返回,并将该节点移动到末尾;如果没有找到,则返回第一个节点的 WebView,并将该节点移动到末尾。
private static final Pools.Pool<X5LBWebView> sPool = new Pools.SynchronizedPool<>(MAX_POOL_SIZE);
sPool.acquire() // 获取一个
sPool.release(webView) //放进去一个
技术点二:池内 WebView 泄漏问题
使用 MutableContextWrapper,如果在某个 Activity 中被使用了就改为该 Activity 的 Context,回收时改为 ApplicationContext。
webView.recycleWebView();
MutableContextWrapper wrapper = (MutableContextWrapper) webView.getContext();
wrapper.setBaseContext(wrapper.getApplicationContext());
sPool.release(webView);
技术点三:当使用池内复用加载成一个正常的页面,返回竟然回到了"about-blank"空白页
起因是:回收到缓存池前,为了降低内存的消耗,调用了 loadUrl("about:blank"),将webview置为空白了,然后调用了 clearHistory() 想清空页面栈,结果却不起作用。
原因:历史记录似乎清除了当前页面之前的所有内容,因此如果浏览器位于“A”页,清除历史记录并导航到“B”页,历史记录将是“A”“B”,而不仅仅是“B”,而是如果在“B”完成加载时清除历史记录,那将只有“B”。
解决方案:了解造成的原因后,就有了解决方法,那就是在onPageFinish再去调用clearHistory(),但要防止onPageFinish重复回调会把正常的打开“A”“B”“C”也给清了。
@Override
public void onLoadFinished(String url) {
if (!TextUtils.isEmpty(url) && url.equals(mUrl) && webViewFactory.isUsePoolWebView()) {
mWebView.clearHistory();
}
}
使用腾讯浏览服务X5-提升Html、Css解析速度和兼容性
引入的 x5 webview ,考虑到最小改动和兼顾 app 内已经使用系统的 WebView 要保持多种 WebView 的共存,同时要两种 WebView 切换的成本变得更低,根据依赖倒置的原则设计了以下的方案。
由 Factory 向使用者提供 WebView , 而外部拿不到具体的实现的 WebView ,只是暴露出基本功能接口供调用。
异常监控-基于移动端侧
按白屏出现的可能类型大致概括为
- JS语法兼容造成
- 网络加载获取数据造成
- 内存不足或者渲染进程异常造成
- 机型/系统版本/chorme版本兼容造成
- Android上疑难杂症
针对上述白屏的原因提供检测和解决的方案
语法不兼容
语法兼容或者错误造成页面无法渲染,使用了高版本的语法对低版本未做兼容,或者引入了三方外部插件外部插件语法存在兼容性问题
我们要拿到h5的错误信息,并将error信息进行上报。查询了下Android WebView的API发现了WebChromeClient这个方法可以满足要求:
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
//获取log的级别
switch (consoleMessage.messageLevel()){
case ERROR://将error信息上报到服务端
LogUtil.logE("webview==",consoleMessage.message()+" level="+ consoleMessage.messageLevel());
LogUtil.uploadH5Error(consoleMessage.message());
break;
}
return super.onConsoleMessage(consoleMessage);
}
这个方法可以拦截JavaScript的console信息,就跟在浏览器里查看一样
资源加载失败
网络资源获取造成的白屏
这种情况与获取的资源有关系,可以通过loading状态和监听WebviewClient方法进行处理
//WebView发起的WEB请求因为网络原因失败时回调
@Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
//WebView发起的WEB请求收到服务器的错误消息时回调
@Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
如何监测离线资源异常还没有思路,需要进一步调研
渲染进程异常
内存导致的白屏或者 WebView 渲染进程异常问题,有时白屏显示,有时界面渲染失败等奇怪现象
起因
要么是因为系统杀死了渲染器以回收急需的内存,要么是因为渲染程序本身崩溃了。
监测
相关的白屏的出现除了语法问题和其他问题,还有可能是渲染进程出现了问题,因为我们白屏主要出现在渲染进程,通过渲染进程找出问题的相关状态信息,对Render Process进行监听。
在 Chromium 中,Browser是单独的进程,Render Process 是单独的进程通过IPC来交互数据,GPU Process仍然为Browser Process的一个线程。
关于Render进程可以在文档里查到关于webview的Termination Handling ,通过该方法检测到渲染进程, 通过实现client里的onRenderProcessGone方法。
//通知宿主应用程序已经退出给定的WebVIEW的渲染过程。
//return true如果宿主应用程序处理进程已退出的情况,
//否则,如果渲染过程崩溃,应用程序将崩溃,或者如果渲染过程被系统杀死则会被杀死。
@Override public void onRenderProcessGone(WebView view, RenderProcessGoneDetail detail)
RenderProcessGoneDetail : 此类提供了有关渲染进程退出原因的更具体信息。应用程序可以使用它来决定如何处理这种情况。
RenderProcessGoneDetail提供了两个方法:
#didCrash():指示是否观察到呈现进程崩溃,或者是否被系统终止。
#rendererPriorityAtExit():返回在渲染器退出时设置的渲染器优先级。
处理
当检测到渲染进程被页面大内存消耗导致被杀或者异常终止时,移除当前webview实例并消耗,否则新渲染进程会失效,通过新的实例加载出新的执行逻辑,可以reload页面或者其他业务逻辑, (而browser进程不死用户无感知),防止当前reload页面如有大内存死循环,可能直接会被系统kill
@Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
if(!detail.didCrash()){
//由于系统内存不足,渲染器被终止。
//通过创建新的WebView实例,应用程序可以正常恢复
if (mywebview != null) {
ViewGroup webViewContainer = (ViewGroup) findViewById(R.id.mywebview);
webViewContainer.removeView(mywebview);
mywebview.destroy();
mywebview = null;
//重新add
}
return true;
}
其他问题造成的页面白屏
这种白屏可能不是内存,js语法、网络加载等造成白屏
适合数据采集的截屏检测
性能测试方法
测量的工具:Chrome Performance
Summary:表示各指标时间占用统计报表
Bottom-Up:表示事件时长排序列表(倒序)
Call tree:表示事件调用顺序列表
Event Log:表示事件发生的顺序列表
什么更影响加载体验
当打开一个h5页面的时候,页面会有较长时间的白屏——此所谓响应慢。白屏时间在h5页面加载的前半程,对整体的体验影响最大