背景
在我们项目开发的时候,遇上了这样的一个场景,处于安全性和性能的考虑,在某个webview中加载的html页面,需要从 原来的远端加载,变成本地加载。原本以为这是一件非常简单的事情,只是将远程的html地址换成html代码文件,放入到资源里面。然后通过本地file协议直接加载就可以了。但事情并不如想象中那么顺利。
image-20180829092853635.png原本能够在远端运行的代码,不能够运行了,这是令人匪夷所思的问题。所以,今天我们就来分享一下解决该问题的方法和过程
分析过程
首先,作为程序猿,我们最常见的方式就是看源码,现在转战源码:
<script type="text/javascript" crossorigin src="//****/log/log.js"></script>
...
<script type="text/javascript" crossorigin src="//image.xxxx.cn/xxxx/xxxx_344a60b.js"></script>
代码我只选择了关键的部分,也隐藏了一些项目数据。我们可以从错误信息和源码里面看出了引发错误的地方。在与引入的资源无法找到,ERR_FILE_NOT_FOUND
,查看到相关资源的源码,标明了src
。
这里的src
并没有使用绝对url,而是使用了相对路径。省略了schema(协议)。而html会为这些资源的url补充上缺失的部分。这里甚至可以把host省略,而webview会自动补充schema和host。这个自动补全功能对于前端开发来说是一件好事,这样在迁移代码的时候,不需要做绝对路径的更改。但是在我们的远端加载切换到本地加载的时候,就会出问题。上述的//image.xxxx.cn/xxxx/xxxx_344a60b.js
在原本的远端加载时会被补充为http://image.xxxx.cn/xxxx/xxxx_344a60b.js
,但是如果是本地加载,就会自动补充file:///image.xxxxxx.cn/xx/xxxx_344a60b.js
,在本地根本没有这个文件,自然加载失败了。
解决方案
1.相对链改绝对链接:在一开始,我们的思维非常惯性,既然说html的资源相对链接是不能使用的,那么我们前端开发只要将所有的相对url改成绝对url就可以了。简单写了一个demo测试后,可行。
但是在项目中,总是有一些磕磕碰碰的事情发生。我们的前端除了自身资源以外,还引入了别的组件(公司其他部门开发的),无法修改源码,所以第一个方法暂时不适用。所以我们就想到了第二种方式,前端开发基本无须改动的方式。
2.本地使用http或其他协议形式加载本地html:在Android里面,我们的webview不仅仅只有loadUrl
这个接口让我们来加载网页。这也是我们本次的解决方式。首先我们来看这个方法的签名:
/**
* Loads the given data into this WebView, using baseUrl as the base URL for
* the content. The base URL is used both to resolve relative URLs and when
* applying JavaScript's same origin policy. The historyUrl is used for the
* history entry.
* <p>
* Note that content specified in this way can access local device files
* (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
* 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
* <p>
* If the base URL uses the data scheme, this method is equivalent to
* calling {@link #loadData(String,String,String) loadData()} and the
* historyUrl is ignored, and the data will be treated as part of a data: URL.
* If the base URL uses any other scheme, then the data will be loaded into
* the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
* entities in the string will not be decoded.
* <p>
* Note that the baseUrl is sent in the 'Referer' HTTP header when
* requesting subresources (images, etc.) of the page loaded using this method.
*
* @param baseUrl the URL to use as the page's base URL. If null defaults to
* 'about:blank'.
* @param data a String of data in the given encoding
* @param mimeType the MIMEType of the data, e.g. 'text/html'. If null,
* defaults to 'text/html'.
* @param encoding the encoding of the data
* @param historyUrl the URL to use as the history entry. If null defaults
* to 'about:blank'. If non-null, this must be a valid URL.
*/
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl)
其中第一个入参baseUrl
比较关键,根据注释的说法,使用baseUrl作为基础URL。这个基础URL是可以使得加载js和其他相关URL使用相同的策略,同时意味着可以使用以下的schema:'http', 'https', 'ftp', 'ftps', 'about' or 'javascript' 。那么正好满足了现在的需求。同时,如果需要在URL里面加入请求参数,也能在这个参数内不齐。
第二个入参data
就是我们的html内容,直接通过读取文件的方式生成String字符串即可。
第三个参数mimeType
实际上和http请求的Content-Type具有相同作用,通常填写text/html
即可。
第四个参数encoding
顾名思义,就是填写整个html的编码格式
第五个参数historyUrl
用于给前端加入history参数。
实现代码
如果对于本地加载的html,不需要关注host,那么直接使用localhost
即可:
webview.loadDataWithBaseURL("http://localhost", htmlData , "text/html","UTF-8", null);
基本能够完美实现以http形式加载本地的页面。
唯一能看出区别的,只有在使用inspect的时候的URL不是传入的baseUrl
:
网友评论