目录
前言1. loadUrl 加载网页地址2. loadData 加载Html字符来显示网页3. loadDataWithBaseURL4. 总结
前言
最近在给一个老项目做64位so文件的适配,当应用发布到应用市场上后,用户反馈64位手机上的网页加载不出内容,但32位的手机上是正常的。
我的第一反应是:网页加载不出内容,那不是网页没做好适配嘛!(把锅甩出去)
后经多方细细排查,是WebView的锅。。。(锅又回来了。。)
WebView 加载网页的方法有三种:loadUrl、loadData、loadDataWithBaseURL,下面只记录本人在这三个方法上遇到的问题,具体WebView的基础使用请参考:
Android – WebView 与 JS 交互方式总结
Android – WebView 支持文件下载的几种方式
1. loadUrl 加载网页地址
先写个网页用于加载测试,test2.html(路径:assets/webpage/test2.html):
<html><body> <h1>Hello, WebView</h1> <p style="color: #f00">This is a simple HTML page loaded into a WebView.</p></body></html>
loadUrl(String url) 加载网页地址:
....//这里省略WebView的其他基础设置mWebView.loadUrl("file:///android_asset/webpage/test2.html");....
显示的内容:
使用 loadUrl(String url) 加载网页,在32位和64位的手机上显示都没有问题。
我的线上应用不是通过url这种方式加载网页的,是通过加载Html字符来加载网页的。
2. loadData 加载Html字符来显示网页
loadData(String data, String mimeType, String encoding)
官方定义:
Load the given data into the WebView. This will load the data into WebView using the data: scheme. Content loaded through this mechanism does not have the ability to load content from the network.
将给定的数据加载到WebView中。这将使用 data: scheme 将数据加载到WebView中。通过此机制加载的内容无法从网络加载内容。
字面意思就是此方法是通过读取Html字符来显示网页的。
/** * 读取 Html字符 * @param fileName 文件名 */private String readAsset(String fileName) { AssetManager assetManager = getAssets(); StringBuilder stringBuilder = new StringBuilder(); try (InputStream inputStream = assetManager.open(fileName); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } } catch (IOException e) { e.printStackTrace(); return null; } return stringBuilder.toString();}private void loadUrl() { ....... StringBuilder stringBuilder = new StringBuilder();//读取Html字符内容stringBuilder.append(readAsset("webpage/test2.html"));mWebView.loadData(String.valueOf(stringBuilder), "text/html; charset=UTF-8", null);.......}
在32位的手机上显示正常,64位的个别手机显示如下:
(线上反馈,Android 10 以上的手机出问题的较多)
由图可知,Html中带颜色的标签p的内容未显示出来。
回到官网看下loadData的入参说明:
data | A String of data in the given encoding. The date must be URI-escaped – ‘#’, ‘%’, ‘’, ‘?’ should be replaced by %23, %25, %27, %3f respectively. |
---|---|
mimeType | The MIMEType of the data. i.e. text/html, image/jpeg |
encoding | The encoding of the data. i.e. utf-8, base64 |
data:此入参中不能出现特殊字符 ‘#’, ‘%’, ‘’, ‘?’ ,需要将其替换成 %23, %25, %27, %3f。
这就为内嵌的css制造了麻烦,因为css中经常使用这4种符号,就如 test2.html 中使用’#'设置了颜色:color: #f00 。
根据官网文档,将 ‘#’ 替换成 ‘%23’:
....StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(readAsset("webpage/test2.html"));mWebView.loadData(String.valueOf(stringBuilder).replace("#", "%23"), "text/html; charset=UTF-8", null);....
这样在64位安卓10以上的手机上就能正常显示了,同时也不影响32位的手机。
在正式的应用里,后台返回的Html字符数据结构都是很复杂的,里面包含css,script等内容,里面用到特殊字符的频率都很大,如果每次加载之前都要对Html做特殊字符替换处理的话,难免会出现乱码等其他问题。
所以这里就需要使用 loadDataWithBaseURL
3. loadDataWithBaseURL
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
官网资料:
Load the given data into the WebView, use the provided URL as the base URL for the content. The base URL is the URL that represents the page that is loaded through this interface. As such, it is used to resolve any relative URLs. The historyUrl is used for the history entry.
将给定的数据加载到WebView中,使用提供的URL作为内容的基本URL。基本URL是表示通过此接口加载的页面的URL。因此,它用于解析任何相对URL。historyUrl用于历史条目。
Note for post 1.0. Due to the change in the WebKit, the access to asset files through “file:///android_asset/” for the sub resources is more restricted. If you provide null or empty string as baseUrl, you won’t be able to access asset files. If the baseUrl is anything other than http(s)/ftp(s)/about/javascript as scheme, you can access asset files for sub resources.
1.0 后的注释。由于 WebKit 的更改,通过"file:///android_asset/"这种方式子资源受到更多的限制。如果你提供null 或空字符串作为baseUrl,你将无法访问资产文件;如果baseUrl不是http(s)/ftp(s)/about/javascript 作为 scheme,你可以访问子资源的文件。
入参说明:
baseUrl | Url to resolve relative paths with, if null defaults to “about:blank” |
---|---|
data | A String of data in the given encoding. |
mimeType | The MIMEType of the data. i.e. text/html. If null, defaults to “text/html” |
encoding | The encoding of the data. i.e. utf-8, us-ascii |
historyUrl | URL to use as the history entry. Can be null. |
从入参上看,loadDataWithBaseURL中并没有对 data 做特殊字符的限制。
....StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(readAsset("webpage/test2.html"));mWebView.loadDataWithBaseURL("", String.valueOf(stringBuilder), "text/html", "UTF-8", "");....
所以,最终使用 loadDataWithBaseURL 完美解决了64位安卓10以上部分手机网页加载不出来的问题。
4. 总结
一开始线上反馈这个问题的时候,我也没有想到是loadData的问题。正式的应用上返回的Html是相当的复杂,里面有Html、css、好几个script,将这些字符内容打印出来在文档上显示时,看着就头疼。(非网页专业的,看着肯定头疼)
想到的最笨的方法,就是将Html、css、script这几个标签拆分开,化复杂为简洁。先将css和script的代码删除,只留下Html,看看 WebView 是否能正常加载出来。结果这一步就不能正常加载,然后我将 Html 中的样式全部去除,只留下文字部分,这样是可以正常显示的,说明问题出在样式上。然后将样式一点点加回到Html中,最终找到是颜色中 ‘#’ 影响了页面的显示。
虽然方法有点儿笨,但好在能排查到问题。