Android WebView性能分析与优化

一、简介

一提到App内的WebView加载网页,大家的第一印象就是:慢、耗流量、体验比原生差。但WebView加载网页也有其天生的优势:动态,跨平台,开发周期短。

那能如何解决WebView加载网页慢和体验差的问题呢?可以思考下面两个问题:

从打开浏览器到网页完全展示都发生了什么?

如何给WebView加载网页提速?

二、整体思维导图

WebView分析优化.png

三、衡量标准

快慢是一个相对量,如何衡量WebView的快慢呢?

3.1 用户体验的时间尺度

从用户角度来看,如下图是2018年份百度移动端的统计数据:

页面放弃率与页面加载事件的关系图.png

2018年份百度移动端的统计数据

谷歌pc相关网页统计.png

Google PC相关网页的数据统计

根据上面的统计折线图,得出如下表格:

页面相关时间统计折线图.png

可以发现:

小于1s,用户更容易接受,关闭率更低。

用户对移动端的容忍比PC端更低,要求更高。

3.2 加载时长标准

加载开始很好界定,当用户点击feed流里面的item开始,就开始计时。

加载结束呢?WebView有个WebViewClient#onPageFinished回调方法,这个方法是在页面完全加载结束时候回调的,但是页面DOM渲染完页面就已经有内容,对于用户来说算是页面已经展示出来了。统计加载时长以DOM渲染完更好些

3.3 统计标准

通过收集真正的用户使用数据,才能更好的根据用户的情况进行优化。那如何才能反应用户的真实情况呢?

通常有两种方式:

平均数,容易被较长的加载时间给拉高,不容易反应真实情况。

中位数,能很好的反应大多数用户的情况,但是中位数的要求较低,可以将其提高到80分位,或者90分位。

在们项目进行数据统计时候可以先采用80分位,检测下优化效果,后续再提高要求,使用更高分位如95分位等。

根据现网数据可知,当95分位的用户页面加载时长为1s以内时,80分位的用户页面加载时长为0.35s以内时,APP内网页的体验最佳。

具体最佳时间可以根据真实的上报数据的统计结果进行调整。

四、问题分析

前端页展示一般分两种:

前后端分离,前端加载资源后,通过js请求展示的数据并在前端渲染展示。

页面直出,页面数据由服务器填充完成后,直接下发到前端,由前端直接展示。

现在较多的采用前后端分离的方式,下面都以这种方式为例讲解。

4.1 WebView渲染过程

WebView渲染大致需要如下几步:

解析 HTML 文件

加载 JavaScript 和 CSS 文件

解析并执行 JavaScript

构建 DOM 结构

加载图片等资源

页面加载完毕

WebView渲染过程

4.2 WebView耗时统计方法

统计可从两方面入手,一是网页层统计,二是App层统计。

4.2.1 网页层统计:WebView中网页耗时统计方法

WebView加载url到完全展示出各个部分耗时情况,可以根据w3c标准中网页performance参数获取具体耗时统计参数信息,详细的页面加载过程见下图:

timing-overview.png

根据performance统计情况可以得出如下数据:

重定向耗时:redirectEnd - redirectStart

DNS查询耗时 :domainLookupEnd - domainLookupStart

TCP链接耗时 :connectEnd - connectStart

HTTP请求耗时 :responseEnd - responseStart

解析dom树耗时 : domComplete - domInteractive

白屏时间 :responseStart - navigationStart

DOMready时间 :domContentLoadedEventEnd - navigationStart

onload时间:loadEventEnd - navigationStart,也即是onload回调函数执行的时间。

4.2.2 App层统计:App层统计WebView耗时

Android可以通过WebViewClient#onPageFinished回调统计页面整个加载时长,开始时间以WebView创建开始算,严格一点可以从feed流中点击item开始算。这个统计只能算整个加载时长,加载到用户可见的时长以DOM渲染完页面为准,后者比前置时长更短一些。前置供参考,以后者为准。

根据上图可以获取的统计数据:

WebView创建耗时:navigationStart - createWebView(以初始化开始时间为准,下同)

交互开始到页面可见耗时:onClickItem - createWebView

页面加载到可见耗时:domContentLoadedEventEnd - createWebView

页面完全加载耗时:onPageFinished - createWebView

4.3 资讯统计数据

测试资讯连接1:测试文章1

测试数据1.png

测试资讯连接2:测试文章2

测试数据2.png

五、优化方案

从统计数据看,WebView首次加载耗时较多2s左右,二次加载耗时也有0.5s左右。

总结数据.png

5.1 离线化

同我们现有的离线包一样,将页面用的公共资源html,css,js等模板化,将模板打成压缩包形成离线包内置或动态下发到App端,在App中访问访问到具体的页面时候优先加载本地的模板资源。

通过WebViewClient#shouldInterceptRequest方法拦截WebView的资源加载,匹配到本地模板中的资源就直接加载本地资源,没有匹配本地模板资源再去加载线上资源。genWebResourceResponse用于实现具体的匹配策略。


overridefunshouldInterceptRequest(view:WebView?,request:WebResourceRequest?):WebResourceResponse?{

returngenWebResourceResponse(request,view)

}

模板注意事项:

精简模板,移除不必要的js、css,进行异步拉取

模板内联js、css,减少io

js尽量放到最后,避免阻碍DOM解析

5.2 数据与模板加载

5.2.1 并行执行:数据请求与模板加并行

虽然进行了本地化网页模板化,但整体的页面加载依然是串行执行的。为了进一步提高页面的加载速度,可以让数据请求由app端代理。使数据加载与模板加载并行执行,待数据加载完成时通过JsBridge回填到网页中。效果如下图:

数据与模板加载.png

5.2.2 数据预加载

既然数据请求已经由app代理了,当然也可以通过一定的策略预加载数据,当页面打开时候直接使用缓存数据。这样整个网页加载过程完全离线化不受网络影响。

本地加载.png

5.3 WebView预创建

由上面统计数据可知,WebView创建与二次创建耗时相差甚远,如下图总结:

总结数据.png

原因是Webview所有的逻辑处理都是通过WebViewProvider来实现的,它需要加载Webview内核,这是一个重量级的操作,内核是以apk的形式存在。而内核加载后在同一页面是共享的,因此后续的初始化时间就很少了。

可以通过预创建WebView来加速这一过程,预创建会消耗一定量的内存,如何平衡预创建和内存消耗问题还需实践把握衡量,具体方式:

WebView池(或统一全局WebView):在app启动时候后台创建WebView池,当app需要展示网页的时候直接拿已创建的WebView,需要在页面销毁时候清除页面数据。池结构如下:

WebView池

预创建WebView注意事项:

WebView初始化需要传context,需要注意内存泄漏

WebView创建需要较大内存,需要注意内存耗费

WebView复用需要清除数据,需要注意状态维护

5.4 模板预热

经过前面几步处理后,网页加载过程可以实现全部本地化后,但每次打开网页的时候还需要重复加载模板数据。DOM解析耗时,如下图:

模板预热

为了避免重复加载模板,则需要在WebView池的基础上,让池中的WebView预先加载本地模板。当需要展示网页时候直接拿到已经加载过本地模板的WebView,并通过JsBridge注入数据。池中结构如下:

模板预热池结构

网页加载的整个过程如下:

模板预热加载

5.5 图片加载

WebView在加载大量图片时候表现不佳,重复进入时还会重复加载图片,体验不好且浪费浏览。

5.5.1 App代理图片加载

该方式需要借助图片加载库如Glide,在WebViewClient#shouldInterceptRequest方法拦截WebView的资源加载,判断要加载的资源url是否为图片,是就走Glide加载并生成加载图片的WebResourceResponse,通过Glide来达到缓存图片目的,避免多次打开页面重复加载线上图片资源,genWebResourceResponse用于实现具体的匹配策略。这种方式有点是不需要前端配合,客户端完全自己处理即可。

在api>=21时,可以通过WebResourceRequest获取请求中的accept字段获取返回值类型,用于区分url类型。

override fun shouldInterceptRequest(view:WebView?,request:WebResourceRequest?):WebResourceResponse?{

val url=request.url.toString()

if(checkImageRequest(request)){

valimageFile=Glide.with(view.context).asFile().load(url).submit().get()

return WebResourceResponse("image/png,*/*","UTF-8",FileInputStream(imageFile))

}

return super.shouldInterceptRequest(view,url)

}

在api<21时,只能通过url来判断来判断类型。

override fun shouldInterceptRequest(view:WebView?,url:String?):WebResourceResponse?{

// 处理资源匹配

return genWebResourceResponse(url,view)

}

5.5.2 hybrid

使用网页和原生控件的混合开发模式,网页中文字部分让WebView渲染,网页中的图片视频等使用原生控件展示。优点即可以避免重复加载图,又能提升图片浏览体验;缺点实现成本高,需要前后端协调处理。今日头条8.0.3版本同样采用了这种方式加载展示图片。

具体思路:

图片展示容器与WebView上下叠放,大小一致

WebView中预留图片占位div

获取网页中图片的url、大小以及位置信息

通过js或其他方式通知App

App加载图片并根据WebView中占位div位置设置原生图片位置

原生控件与WebView同步滚动

六、总结

6.1 、从webview入手,减少加载时间或者说复用加载时间

全局WebView

方法:

    在客户端刚启动时,就初始化一个全局的WebView待用,并隐藏;
    当用户访问了WebView时,直接使用这个WebView加载对应网页,并展示。

这种方法可以比较有效的减少WebView在App中的首次打开时间。当用户访问页面时,不需要初始化WebView的时间。

当然这也带来了一些问题,包括:

    额外的内存消耗。
    页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。

    【参考东软专利 - 加载网页的方法及装置 CN106250434A】

客户端代理数据请求

方法:

    在客户端初始化WebView的同时,直接由native开始网络请求数据;
    当页面初始化完成后,向native获取其代理请求的数据。

此方法虽然不能减小WebView初始化时间,但数据请求和WebView初始化可以并行进行,总体的页面加载时间就缩短了;缩短总体的页面加载时间:

    【参考腾讯分享:70%以上业务由H5开发,手机QQ Hybrid 的架构如何优化演进?】

还有其他各种优化的方式,不再一一列举,总结起来都是围绕两点:

    在使用前预先初始化好WebView,从而减小耗时。
    在初始化的同时,通过Native来完成一些网络请求等过程,使得WebView初始化不是完全的阻塞后续过程。

6.2、资源本地化

6.2.1、在webview的加载过程中,我们通过重写方法 shouldInterceptRequest 可以拦截到不同的请求

只需要把一些常用的资源如cs、js文件打包到apk中,然后做本地的替换,就回使用本地的资源,这样减少了重复请求的时间,优化了webview的加载时间

6.2.2、本地离线包的形式

webview 可以加载网络资源,那么也是可以加载本地的资源,在apk 启动的时候,我们可以把整个前端代码文件下载解压到本地的文件路径中,然后通过file:///...index.html 去打开本地的资源

6.2.3、拦截webview的请求,shouldInterceptRequest 不仅可以拦截资源文件,也是可以拦截html中的ajax请求,拦截请求后,可以使用android常见的okhttp或者retrofit等请求框架发起请求,可以添加需要的头信息,或者修改请求参数,然后返回response再返回给webview,这样就做到了动态修改webview中的请求信息,这里需要注意的是,post请求的body是无法拦截的到的,所以就需要把body参数放置到header中拦截,然后自己处理后再拼装即可

Android中WebView还存在较大的优化空间,可以进一步提升资讯、活动页等h5页面的浏览体验。本文涉及的优化方式仅是方向性的,为后续Android App的WebView优化提供方向性指引,实际操作会涉及到多端配合,细节较多,需要不断迭代优化。

参考:

WebView内存泄漏--解决方法小结 - 简书

今日头条品质优化 - 图文详情页秒开实践

WebView性能、体验分析与优化 - 美团技术团队

百度APP-Android H5首屏优化实践

Android开发——H5容器加载速度优化方案_SEU_Calvin的博客-CSDN博客_h5加载速度优化

调研——腾讯VasSonic,优化H5页面打开速度_吹泡泡的星星koky的博客-CSDN博客

WebView 优化(1)—— 缓存管理、回收复用、网页秒开、白屏检测_锐湃的博客-CSDN博客_android webview秒开

H5加速、WebView加速、Hybrid 常见方案对比_广靓的博客-CSDN博客_webview预热

满满的WebView优化详解,让你的H5实现秒开体验。_普通网友的博客-CSDN博客

android性能优化(三)之Webview优化_qingtiantianqing的博客-CSDN博客_android webview优化

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值