Android WebView加载优化

1.前言

最近几年关于原生WebView与H5混合开发的项目越来越多,这种开发带来了很多便利,但也会有一些缺点,比如说通过WebView加载H5会有一定的卡顿现象,会影响用户体验。下面本文就此问题一一展开讨论。

2. 场景

根据日常需求一般是通过webView.loadUrl()方法加载指定的网页,其大概流程如下:

image.png

  • 创建WebView:通过Java代码创建WebView,设置相关的参数和属性
  • 发起请求:通过底层内核会先检查缓存,然后决定是否创建对应的请求,建立对应的http请求。
  • 页面解析:下载对应的DOM树和相关资源。
  • 内容处理:根据上面的资源和树形结构,进行渲染展示。
    以上是整个网页请求的大概过程,优化也是在这几个过程中分别作出细致对应的方案。

2.1 创建WebView

WebView创建的时机会对整个流程是有影响的,Webview的启动相对来说比较耗时,因此这个时候我们可以采用提前启动Webview,这样就可以在加载网页的时候已经准备好。比如我在做Browser的时候发现有Native页面点击网页icon,启动相对来说比较慢,那么采用的策略就是进入Native页面后,一段时间后提前创建Webview。一开始本以为这样就可以,但是浏览器在tab页面可以新建多个网页加载器,也就是会有多个webview。显然光是提前创建也不太好,后面采用创建一个WebView的池子来管理这些Webview

1/** 2 * webview 复用池 3 */ 4public class WebViewPool { 5 private static final String DEMO_URL = "https://www.baidu.com"; 6 private static final String APP_CACAHE_DIRNAME = "webCache"; 7 private static List<WebView> available = new ArrayList<>(); 8 private static List<WebView> inUse = new ArrayList<>(); 9 private static final byte[] lock = new byte[]{}; 10 private static int maxSize = 2; 11 private int currentSize = 0; 12 private static long startTimes = 0; 13 private static volatile WebViewPool instance = null; 14 15 public static WebViewPool getInstance() { 16 if (instance == null) { 17 synchronized (WebViewPool.class) { 18 if (instance == null) { 19 instance = new WebViewPool(); 20 } 21 } 22 } 23 return instance; 24 } 25 26 /** 27 * Webview 初始化 28 * 最好放在application oncreate里 29 */ 30 public static void init(Context context) { 31 for (int i = 0; i < maxSize; i++) { 32 WebView webView = new WebView(context); 33 initWebSeting(context, webView); 34 //webView.loadUrl(DEMO_URL); 35 available.add(webView); 36 } 37 } 38 39 /** 40 * 获取webview 41 */ 42 public WebView getWebView(Context context) { 43 synchronized (lock) { 44 WebView webView; 45 if (available.size() > 0) { 46 webView = available.get(0); 47 available.remove(0); 48 currentSize++; 49 inUse.add(webView); 50 } else { 51 webView = new WebView(context); 52 initWebSeting(context, webView); 53 inUse.add(webView); 54 currentSize++; 55 } 56 return webView; 57 } 58 } 59 60 /** 61 * 回收webview ,不解绑 62 * 63 * @param webView 需要被回收的webview 64 */ 65 public void removeWebView(WebView webView) { 66 webView.loadUrl(""); 67 webView.stopLoading(); 68 webView.setWebChromeClient(null); 69 webView.setWebViewClient(null); 70 webView.clearCache(true); 71 webView.clearHistory(); 72 synchronized (lock) { 73 inUse.remove(webView); 74 if (available.size() < maxSize) { 75 available.add(webView); 76 } else { 77 webView = null; 78 } 79 currentSize--; 80 } 81 } 82 83 /** 84 * 回收webview ,解绑 85 * 86 * @param webView 需要被回收的webview 87 */ 88 public void removeWebView(ViewGroup viewGroup, WebView webView) { 89 viewGroup.removeView(webView); 90 webView.loadUrl(""); 91 webView.stopLoading(); 92 webView.setWebChromeClient(null); 93 webView.setWebViewClient(null); 94 webView.clearCache(true); 95 webView.clearHistory(); 96 synchronized (lock) { 97 inUse.remove(webView); 98 if (available.size() < maxSize) { 99 available.add(webView); 100 } else { 101 webView = null; 102 } 103 currentSize--; 104 } 105 } 106 107 /** 108 * 设置webview池个数 109 * 110 * @param size webview池个数 111 */ 112 public void setMaxPoolSize(int size) { 113 synchronized (lock) { 114 maxSize = size; 115 } 116 } 117 118 private static void initWebSeting(Context context, WebView webView) { 119 ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 120 ViewGroup.LayoutParams.MATCH_PARENT); 121 webView.setLayoutParams(params); 122 WebSettings webSettings = webView.getSettings(); 123 //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript 124 webSettings.setJavaScriptEnabled(true); 125 // 若加载的 html 里有JS 在执行动画等操作,会造成资源浪费(CPU、电量) 126 // 在 onStop 和 onResume 里分别把 setJavaScriptEnabled() 给设置成 false 和 true 即可 127 128 //支持插件 129 // webSettings.setPluginsEnabled(true); 130 131 //设置自适应屏幕,两者合用 132 webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 133 webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小 134 135 //缩放操作 136 webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。 137 webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放 138 webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件 139 140 //其他细节操作 141 webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); //关闭webview中缓存 142 webSettings.setAllowFileAccess(true); //设置可以访问文件 143 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 144 webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片 145 webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式 146 147 webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能 148 webSettings.setDatabaseEnabled(true); //开启 database storage API 功能 149 webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能 150 151 String cacheDirPath = context.getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME; 152 webSettings.setAppCachePath(cacheDirPath); //设置 Application Caches 缓存目录 153 154 //页面白屏问题 155 webView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent)); 156 webView.setBackgroundResource(R.color.white); 157 } 158}

需要Webview的时候直接在此pool中取即可。

2.2 发起请求

在Webview真正发起请求的之前,Webview会进行缓存检查,而对于Webview来说可以可以设置缓存webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
在DOM树和相关图片资源下载的时候,这个其实是相对来说比较耗时的,那么一般有以下几个优化的思路

  • DOM树形成固定的模板,通过http请求出具体数据,填充到模板
  • 资源拦截:通过shouldInterceptRequest拦截一些图片和css资源,通过本地返回。
  • 图片加载: 在Webview进行初始化的时候设置默认不可以加载图片,在pagefinished方法中设置图片自动加载。

2.3 内容处理

关于内容处理这个其实需要服务器配合,简单来说当用户通过WebView来加载某个网页A,此时网页会出现一些内容,当用户点击此网页中内容的时候会进入下一页B。那么在用户刚进入A的时候,此时服务器是可以预知到下一个页面B的内容,因为B的链接是知道的,那么此时服务器可以通过预加载的方式请求到相关内容,内容回来之后无论是置于CDN还是推到客户端都可以随业务来定,这样相对来说是会提高加载速度的。

总结

整体来说WebView的加载优化大概以上几个方向:

  • 活用缓存
  • 提前加载
  • 尝试拦截
    这是Webview这块可以处理的,当然如果可以自己处理内核,那么可以优化的空间会更多,在此就不做展开。
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Flutter

在Flutter加载网页?也是有WebView的哦,和Android一样1.添加依赖dependencies:flutter\_webview\_plugin:^0.2.122.导入库import'import'package:flutter\_webview\_plugin/flutter\_webview\_plug