社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
作为一名Android工程师,我们在使用Webview时候遇到过很多很多的坑,比如加载白屏,超时加载,卡顿等等,一系列的问题,每次在使用Webview的时候脑海中是不是出现过无数的草泥马。今天我们将从性能、内存消耗、体验、安全几个维度,来系统的分析客户端默认WebView的问题,以及对应的优化方案。彻底解决困扰在Android工程师面前的这个难题。
1.性能问题
打开速度比native慢
对于一个普通用户来讲,打开一个WebView通常会经历以下几个阶段:
交互无反馈——》到达新的页面,页面白屏——》页面基本框架出现,但是没有数据——》页面处于loading状态——》出现所需的数据
如果从程序上观察,WebView启动过程大概分为以下几个阶段:
WebView初始化
1.当App首次打开时,默认是并不初始化浏览器内核的;只有当创建WebView实例的时候,才会创建WebView的基础框架。
所以与浏览器不同,App中打开WebView的第一步并不是建立连接,而是启动浏览器内核。
2.在浏览器中,我们输入地址时(甚至在之前),浏览器就可以开始加载页面。而在客户端中,客户端需要先花费时间初始化WebView完成后,才开始加载。
解决方案
方案1,全局WebView
在客户端刚启动时,就初始化一个全局的WebView待用,并隐藏;当用户访问了WebView时,直接使用这个WebView加载对应网页,并展示。这种方法可以比较有效的减少WebView在App中的首次打开时间。当用户访问页面时,不需要初始化WebView的时间。当然这也带来了一些问题,包括:额外的内存消耗。页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。
方法2,客户端代理数据请求
在客户端初始化WebView的同时,直接由native开始网络请求数据;当页面初始化完成后,向native获取其代理请求的数据。此方法虽然不能减小WebView初始化时间,但数据请求和WebView初始化可以并行进行,总体的页面加载时间就缩短了;缩短总体的页面加载时间:
还有其他各种优化的方式,不再一一列举,总结起来都是围绕两点:
在使用前预先初始化好WebView,从而减小耗时。
在初始化的同时,通过Native来完成一些网络请求等过程,使得WebView初始化不是完全的阻塞后续过程。
建立连接/服务器处理
在页面请求的数据返回之前,主要有以下过程耗费时间。
DNS
connection
服务器处理解决方案
解决方案
方案一 DNS采用和客户端API相同的域名
(DNS:域名系统,万维网上作为域名和IP地址相互映射的一个分布式数据库,访问网址时会先解析域名,域名分为动态解析和静态解析,静态解析失败会继续动态解析)DNS会在系统级别进行缓存,对于WebView要请求的地址,如果使用的域名与native的API相同,则可以直接使用缓存的DNS而不用再发起请求去进行域名解析所浪费时间。以美团为例,美团的客户端请求域名主要位于api.meituan.com,然而内嵌的WebView主要位于 i.meituan.com。当我们初次打开App时:客户端首次打开都会请求api.meituan.com,其DNS将会被系统缓存。然而当打开WebView的时候,由于请求了不同的域名,需要重新获取i.meituan.com的IP。根据统计,至少10%的用户打开WebView时耗费了60ms在DNS上面,如果WebView的域名与App的API域名统一,则可以让WebView的DNS时间全部达到1.3ms的量级。静态资源同理,最好与客户端的资源域名保持一致。
方案二 同步渲染采用chunk编码
方案三 页面框架渲染
页面在解析到足够多的节点,且所有CSS都加载完成后进行首屏渲染。在此之前,页面保持白屏;在页面完全下载并解析完成之前,页面处于不完整展示状态。
解决方案:
Webview内存消耗较大,
除了打开的速度,WebView通常体验也没有native的实现更好,我们可以找到以下几个例子:
在WebView中,长按文字会使得WebView默认开始选择文字;长按链接会弹出提示是否在新页面打开。
解决方法:可以通过给body增加CSS来禁止这些默认规则。
在WebView中,click通常会有大约300ms的延迟(同时包括链接的点击,表单的提交,控件的交互等任何用户点击行为)。
唯一的例外是设置的meta:viewpoint为禁止缩放的Chrome(然而并不是Android默认的浏览器)。
解决方法:使用fastclick一般可以解决这个问题。
在很多需求中会有一些吸顶的元素,例如导航条,购买按钮等;当页面滚动超出元素高度后,元素吸附在屏幕顶部。
这个功能在PC和native中都能够实现,然而在WebView中却成了难题:
在页面滚动期间,Scroll Event不触发
不仅如此,WebView在滚动期间还有各种限定:
这些限制大部分是不可突破的,但至少对于吸顶功能还是可以做一些支持:
解决方法:
通常WebView并不能直接接触到底层的API,因此比较稳定;但仍然有使用不当造成整个App崩溃的情况。
目前发现的案例包括:
由于WebView加载的页面代码是从服务器动态获取的,这些代码将会很容易被中间环节所窃取或者修改,其中最主要的问题出自地方运营商(浙江尤其明显)和一些WiFi。
我们监测到的问题包括:
无视通信规则强制缓存页面。
header被篡改。
页面被注入广告。
页面被重定向。
页面被重定向并重新iframe到新页面,框架嵌入广告。
HTTPS请求被拦截。
DNS劫持。
针对页面注入的行为,有一些解决方案:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!