摘要
1.简单介绍WebView的缓存,详细参考H5 缓存机制浅析 移动端 Web 加载性能优化
2.新闻中使用WebView涉及到的点。
3.Android WebView的Js对象注入漏洞解决方案
WebView的缓存
目前缓存有以下6种,我们只介绍两种。
Dom Storage(Web Storage)存储机制
DOM 存储被设计为用来提供一个更大存储量、更安全、更便捷的存储方法,从而可以代替掉将一些不需要让服务器知道的信息存储到 cookies 里的这种传统方法。
Dom Storage 是通过存储字符串的 Key/Value 对来提供的,并提供 5MB (不同浏览器可能不同,分 HOST)的存储空间(Cookies 才 4KB)。
Dom Storage 存储的数据在本地,不像 Cookies,每次请求一次页面,Cookies 都会发送给服务器。
简单介绍一下Dom Storage,它包含两种机制:
在Android端想要使用Dom Storage只要进行如下设置:
1 2 3
| WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setDomStorageEnabled(true);
|
有上述那么多缓存机制,为何新闻正文的图片加载,还是让Native APP做缓存?
点此查看各版本浏览器对存储容量的描述详情
通过查看存储的容量描述很清楚的看到,存储容量就特别少,并不足够支撑新闻页面的图片存储。
Indexed Database 存储机制
IndexedDB 是一种灵活且功能强大的数据存储机制,它集合了 Dom Storage 和 Web SQL Database 的优点,用于存储大块或复杂结构的数据,提供更大的存储空间,使用起来也比较简单。可以作为 Web SQL Database 的替代。不太适合静态文件的缓存。
- 以key-value 的方式存取对象,可以是任何类型值或对象,包括二进制。
- 可以对对象任何属性生成索引,方便查询。
- 较大的存储空间,默认推荐250MB(分 HOST),比 Dom Storage 的5MB 要大的多。
- 通过数据库的事务(tranction)机制进行数据操作,保证数据一致性。
- 异步的 API 调用,避免造成等待而影响体验。
Android 在4.4开始加入对 IndexedDB 的支持,只需打开允许 JS 执行的开关就好了。
1 2 3
| WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true);
|
新闻WebView相关
在点击正文滑动到任何位置,退出正文页再进入时,发现可以停留在上次滑动停止的位置。
一开始我认为是缓存的主要作用,但我还是太天真。其实在正文页面onDestroyView()时,我们有调用
1
| int scrollY = webView1.getScrollY();
|
获取WebView现在滚动的位置,记录下来。
在正文页面渲染,比如WebView实现的OnPreDrawListener#onPreDraw()方法中调用
1
| webView.scrollTo(webView.getScrollX(), scrollY);
|
使WebView定位到退出之前的位置。
注意:
WebView的实际内容的高度通过:
1
| int height = (int) (webView1.getContentHeight() * webView1.getScale());
|
有一些数据内容是异步回调取回的,有可能获取的scrollY大于当前WebView实际的内容高度height,那么应该考虑不定位,而是scrollY=0的位置开始展示。
计算当前屏幕展示的图片
在WebView的WebViewClient#onPageFinished()方法中加载图片,调用js方法
1 2 3 4 5 6
| function updateImgCount(count) { if (!count || count <= 0) { return; } imgContentCount = count; }
|
保存图片数量imgContentCount,并且调用requestImgLoading(true)方法强制加载图片。
在WebView滚动时,通过js监听WebView的滚动:
1 2 3
| window.onscroll = function() { requestImgLoading(false); }
|
其实用WebView#onScrollChanged()方法监听滚动,测试快速滑动时,回调也还算及时,我觉得两种方案都可以。但由于是通过js方法计算当前屏幕内需要加载的图片,所以还是使用js监听滚动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| function requestImgLoading(force) { if (!window.news) { return; } // 图片数为0则返回 if (imgContentCount == 0) { return; } if (!imgContents) { imgContents = new Array(); } var screenHeight = 3 * window.screen.height; var screenHeight = screenHeight / window.devicePixelRatio; var start = -1; var end = -1; // 循环遍历图片节点 for (var i = 0; i < imgContentCount; i++) { if (!imgContents[i]) { imgContents[i] = document.getElementById("imgContent_" + i); } if (!imgContents[i]) { break; } // getBoundingClientRect()方法返回元素的大小及其相对于视口的位置,返回值为DOMRect; // DOMRect除了 width 和 height 外的属性都是相对于视口的左上角位置而言的; var r = imgContents[i].getBoundingClientRect(); // r.top < screenHeight认为在屏幕内 if (r.bottom > 0 && r.top < screenHeight) { start = i; if (r.bottom >= screenHeight) { end = i; } break; } } if (start >= 0 && end == -1) { for (var i = start; i < imgContentCount; i++) { if (!imgContents[i]) { imgContents[i] = document.getElementById("imgContent_" + i); } if (!imgContents[i]) { break; } end = i; var r = imgContents[i].getBoundingClientRect(); if (r.bottom >= screenHeight) { break; } } } if (start >= 0 && end == -1) { end = start; } if (force || lastRequestLoadImgStart != start || lastRequestLoadImgEnd != end) { lastRequestLoadImgStart = start; lastRequestLoadImgEnd = end; if (start != -1 && end != -1) { var allLoaded = true; for (var i = start; i <= end; i++) { allLoaded = true == imgContents[i].loaded; if (!allLoaded) { break; } } if (!allLoaded) { window.news.requestImgLoading(start, end); } } } }
|
通过js计算出当前屏幕展示的图片节点,在调用Native方法去发送图片请求,拿到请求后的本地路径通过position设置给图片节点。
参考
Android WebView:性能优化不得不说的事
H5 缓存机制浅析 移动端 Web 加载性能优化
Web Storage API