前言

如下图打开一个WebView通常会经历以下几个阶段:

上图中webview初始化阶段,对于用户来说是无反馈。当App首次打开时,默认是并不初始化浏览器内核的,只有当创建WebView实例的时候,才会创建WebView的基础框架。App中打开WebView的第一步并不是建立连接,而是启动浏览器内核。

webview初始化时间数据对比

首次打开时间 二次打开时间
498ms 142ms

可以看出webview首次打开时间比二次打开时间3倍还多,初次打开webview时间长的原因是首次打开webview的时候要加载webview的内核。

webview预创建

由上面的对比可知,首次打开webview的时间是很长的,如果在业务打开webview的时候再创建webview加载url,打开的速度会比较慢。

  • 可以在app初始化后,创建一个webview的缓存池,当业务需要使用webview的时候从缓存池中获取webview,由于缓存池中的webview是已经初始化了的,所以业务打开webview相当于二次打开webview。

  • 由于webview只能在主线程中创建,可以在为了不影响冷启动性能,可以使用idleHandler方法在主线程空闲时创建webview

1、创建webview并缓存

    fun preCreate(size: Int, application: Context) {this.size = sizeodysContext = MutableContextWrapper(application)createWebview()}mainMessageQueue?.addIdleHandler {//看下cacheWebs size中数量,如果cacheWebs的size比preCreate的size少,则创建webview并添加if (cacheWebs.size < size) {odysContext?.let {cacheWebs.add(SoftReference(WebView(it)))}}false}
  • 上面cacheWebs中使用软引用来来存放webview的实例,这样在内存不足时可以进行释放,添加webview到缓存放在idleHandler中。
  • 上面对context使用MutableContextWrapper进行一层包装,MutableContextWrapper可以替换context,因为预创建webview的时候不能使用activity的context(预创建的webview不属于任何activity,如果使用activity会内存泄漏),这里使用的是applicationContext进行webview初始化。
  • MutableContextWrapper代码如下,提供了setBaseContext方法来设置context,这样在activity中获取webview时就可以进行替换了。
public class MutableContextWrapper extends ContextWrapper {public MutableContextWrapper(Context base) {super(base);}/*** Change the base context for this ContextWrapper. All calls will then be* delegated to the base context.  Unlike ContextWrapper, the base context* can be changed even after one is already set.* * @param base The new base context for this wrapper.*/public void setBaseContext(Context base) {mBase = base;}
}

2、获取webview实例
在activity中获取webview实例,这里activity一般定义为webviewActivity,如下

    /**** 获取缓存中的webview,这里的context必须是activity的context*/fun fetchCacheWebview(context: Context, index: Int): WebView? {return if (cacheWebs.isEmpty() || cacheWebs.size <= index) {//创建一个新的webviewWebView(context)} else {//拿到index的webview,并且移除掉cache中的这个webview,不然其他地方如果又拿了就出问题val ref = cacheWebs[index]cacheWebs.removeAt(index)//移除后再创建一个缓存的webview,保证缓存池中的webview数量不变createWebview()//这里替换掉webview的contextval web = ref.get()//replace contextreplaceContext(web, context)return web}}

注意获取cacheWebs中的activity时需要activity的context,并且使用replaceContext(web,context) 方法替换掉原来webview中的application context

    private fun replaceContext(webView: WebView?, context: Context) {webView?.let {val cot = it.contextif (cot is MutableContextWrapper?) {cot.baseContext = context}}}
  • 在获取到cacheWebs中的webview实例后,要将这个实例从cacheWebs中移除,防止其他地方再获取这个实例。
  • 在移除cacheWebs中的webview实例后,要再往缓存中加webview实例,保证缓存池中webview的缓存数量。

3、activity中如何使用预创建的webview
正常来说webview的使用方式有在xml中定义webview,和动态添加webview。

1、往布局里面加WebView

<WebViewandroid:id="@+id/webview"android:layout_width="match_parent"android:layout_height="match_parent"
/>

2、动态添加webview


val webView = WebView(activityContext)
setContentView(webView)
  • 由于要使用预创建的webview,所以直接在xml布局中添加webview的话就无法使用预创建的webview,因为xml中的元素解析生成对象是在setContentView()方法中,会在xml解析时自动生成webview对象。
  • 如果想使用xml的方式并且使用预加载的webview,可以用下面的方式:
    <FrameLayoutandroid:id="@+id/webviewContainer"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />

然后在代码中进行如下设置:

    //先获取预创建的webviewval webview=OdysManager.fetchCacheWebview(context,0)//将webview 添加到webviewContainer中webviewContainer.addView(webview, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.MATCH_PARENT))

动态添加预创建的webview

//先获取预创建的webviewval webview=OdysManager.fetchCacheWebview(context,0)//将webview 添加到webviewContainer中setContentView(webview)

3、销毁webview
在activity的onDestory中对webview进行销毁,销毁webview时先替换掉webview的context为applicationContext如下:

    override fun onDestroy() {super.onDestroy()//替换掉context并且销毁if (webView != null) {webView.stopLoading()replaceContext(webView, context)webView.loadUrl("about:blank")webView.destroy()}}

数据对比

以创建webview之前开始,到onPageStarted的时间戳统计数据如下

  • 1、不使用预创建webview
机型 时间(ms)
Honor 20 648
Honor 20 624
Honor 20 593
Honor 20 565
Honor 20 515

2、使用预创建webview

机型 时间(ms)
Honor 20 350
Honor 20 297
Honor 20 310
Honor 20 323
Honor 20 254

代码仓库

https://github.com/hankinghu/odys

参考

1、https://tech.meituan.com/2017/06/09/webviewperf.html

webview性能优化—webview预创建相关推荐

  1. Android WebView 性能优化

    原文出处:http://motalks.cn/2016/09/11/Android-WebView-JavaScript-3/ WebView相关阅读 Android WebView 和 javaSc ...

  2. WebView开发(三):WebView性能优化

    一.Android WebView开发(一):基础应用 二.Android WebView开发(二):WebView与Native交互 三.Android WebView开发(三):WebView性能 ...

  3. WebView性能优化的那些事儿……

    有个精通硬件,后台,前端的大神朋友最近想做一个Web APP,于是便来求助于我,让我受宠若惊啊.倒也趁此机会恶补了一下关于WebView的那些知识.恩-- 前言 现在 App 嵌入 H5 页面已经是稀 ...

  4. echarts一次渲染两个图_一次 Flutter WebView 性能优化

    本文记录了基于 WebView 的 Flutter 可视化库:echarts_flutter 的一次优化加载性能的过程. 对于任何基于 WebView 的组件,html 的加载都是关乎性能的一个重要环 ...

  5. WebView性能优化--独立进程

    Android允许一个app同时存在多个进程,可以根据需要把不同的模块放到不同进程中处理. 一.WebView独立进程的好处 1.有效增大App的运存,减少由webview引起的内存泄露对主进程内存的 ...

  6. SQL SERVER 性能优化四: 创建分区表

    1.整体介绍 1.1 分区表概念:分区表值得是逻辑上是一个表,物理上被存储到不同的磁盘文件中. 1.2 优势:提高查询性能:提高稳定性:便于管理:对于大数据量表备份更方便. 1.3 建立分区表主要包含 ...

  7. ajax预加载html seo,前端性能优化 — JS预加载和懒加载

    JS预加载 需求:有时我们需要实现例如快速快速切换页面.图片之类的功能时,能尽快的加载出我们所需的图片会极大提升用户体验,这时用预加载将图片先缓存到浏览器,用户使用需显示图片时无疑会顺畅很多. 核心: ...

  8. 前端性能优化之预加载

    网络连接的快慢,是前端性能的瓶颈之一,在这里我们能做些什么呢,下面介绍几个通过浏览器特性来很容易提高资源加载速度的方法: DNS prefetching DNS解析的速度可用通过下面的标签来进行预解析 ...

  9. 前端性能优化:预渲染

    预渲染 可以通过预渲染将下载的文件预先在后台渲染,可以使用以下代码开启预渲染 <link rel="prerender" href="http://poetries ...

最新文章

  1. JS break语句和continue语句
  2. 炒鸡简单,带你快速撸一遍Numpy代码!
  3. K-means算法在手写体数字图像数据上的使用示例-代码详解
  4. 函数指针指向类的静态成员函数
  5. sass之mixin的全局引入(vue3.0)
  6. 成本预算的四个步骤_工业企业成本管理之成本控制体系的构建
  7. linux常用查看磁盘空间大小的命令
  8. Kali下TheFatRat工具的安装教程
  9. xshell链接相当的慢,怎么解决
  10. 图片OCR进行在线的文字识别
  11. java实现登录注册界面
  12. wuauclt.exe进程和wuauclt病毒的查杀清理方法
  13. SQI SERVER2016安装选项
  14. JAVA时间字符串去空格、冒号和横杠
  15. Pinned Memory 多设备异步拷贝
  16. Python 100道基础入门练习题(附答案)【这期完结】
  17. CPU当中的分支预测
  18. jenkins 流水线参数化构建例子
  19. Token的简单解释
  20. springboot slf4j log4j2 动态创建日志的方法

热门文章

  1. 4种大文件传输工具和软件,用于共享大文件
  2. 看美国无线路由器品牌用户满意度排行榜
  3. 关于能连上网却打不开网页的问题
  4. puzzle(0711)《机关排布》接水管、搭桥
  5. document.forms[0].submit();和document.forms[0].action = ““;问题
  6. 用html做完整网页效果
  7. matlab上机绘图实验心得,matlab实验心得总结
  8. 家谱世表怎么写?4个要点不注意,会让人贻笑大方的
  9. SpringBoot-集成Shiro
  10. linux下非root用户如何修改root权限的文件