IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android WebView性能分析与优化 -> 正文阅读

[移动开发]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用于实现具体的匹配策略。

override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
     return genWebResourceResponse(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)) {
        val imageFile = 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同步滚动

六、总结

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

参考文章:

【白皮书4.0解读】页面加载速度的重要性

Does Page Load Time Really Affect Bounce Rate?

10的幂:用户体验的时间指标

web页面加载用户等待时间的性能指标

应用:前端性能监控performance

深入理解前端性能监控—Performance

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

WebView性能、体验分析与优化

VasSonic

ht-candywebcache-android

Android混合开发之——WebView中使用原生组件替换标签元素

iOS 牛牛圈文章详情页hybrid方案预研

w3c标准

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 16:33:51  更:2021-07-28 16:33:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 8:55:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码