在开发中,有时候 App 的本地资源(如离线 html 文件)等会用 Webview 去加载,如果同时需要考虑多语言国际化问题,应该如何处理呢?
需求说明
比如现在有这样一个需求,一般 App 都有相关的权限申请说明,现在要求用 Webview 去加载本地的 html 资源文件并显示相关权限声明,且支持多语言国际化。
问题分析
我们知道,res
目录可以方便进行多语言国际化的支持。那现在可以把权限声明(perm_state.html
)文件放置于 /res/raw/
的不同语言目录中,以实现国际化:
简体中文的支持:/res/raw-zh-rCN/perm_state.html
:
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
| <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"/> <title>权限声明</title> <style type="text/css"> body { padding: 0 10px; background-color: #0; }
a.title { font-size: 16px; font-weight: bold; }
a.content { font-size: 14px; } </style> </head> <body> <p> <a class="title">接收短信(RECEIVE_SMS)</a><br/> <a class="content">解析短信中的验证码需要能够接收短信权限。</a> </p> </body> </html>
|
默认英文支持:/res/raw/perm_state.html
:
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
| <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"/> <title>Permission Statement</title> <style type="text/css"> body { padding: 0 10px; background-color: #0; }
a.title { font-size: 16px; font-weight: bold; }
a.content { font-size: 14px; } </style> </head> <body> <p> <a class="title">Receive SMS permission</a><br/> <a class="content">Receive SMS permission should be granted for parsing SMS.</a> </p> </body> </html>
|
ResUtils#loadRawRes
用来加载 /res/raw
资源文件中的内容:
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
| public class ResUtils {
public static String loadRawRes(Context context, @RawRes int rawId) { InputStream is = null; String data = ""; try { is = context.getResources().openRawResource(rawId); byte[] buffer = new byte[is.available()]; is.read(buffer); data = new String(buffer); } catch (IOException e) { XLog.e("Error occurs when open raw file, id = " + rawId, e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return data; } }
|
接下来就是用 Webview
加载 html
资源文件:
1 2 3 4
| public void loadHtmlData() { String data = ResUtils.loadRawData(context, R.raw.perm_state); weview.loadData(data, "text/html", "utf-8"); }
|
以上就是一种解决方案,那么,有没有可以优化的空间呢?
观察到,/res/raw-zh-rCN/perm_state.html
和 /res/raw/perm_state.html
是有相同的 css
样式的,那么能否将 css
样式抽取出来共用呢?当然是可以的 ~
方案优化
把 css
样式抽取出来生成 perm_state_style.css
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| body { padding: 0 10px; background-color: #0; }
a.title { font-size: 16px; font-weight: bold; }
a.content { font-size: 14px; }
|
/res/raw-zh-rCN/perm_state.html
修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"/> <title>权限声明</title> <link href="perm_state_style.css" type="text/css" rel="stylesheet"/> </head> <body> <p> <a class="title">接收短信(RECEIVE_SMS)</a><br/> <a class="content">解析短信中的验证码需要能够接收短信权限。</a> </p> </body> </html>
|
其他的 perm_state.html
修改方式跟上述一致就不再赘述。
WebView
有以下两个方法函数:
loadData(String data, String mimeType, String encoding)
: 直接使用 webview
加载 data
包含的数据。
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
: 将 baseUrl
作为基础路径的前提下,使用 webview
加载 data
包含的数据。
我们看到 <link href="perm_state_style.css"/>
是直接引用的 perm_state_style.css
,所以可以使用 loadDataWithBaseURL
方法把 css
文件放置于 baseUrl
所代表的目录下即可实现 html
文件中对 css
样式的引用和加载。
那么,perm_state_style.css
到底放哪儿呢? 有两种方案。
css 置于 res/raw 目录
将 css
置于 /res/raw
目录,即生成 /res/raw/perm_state_style.css
。
使用 WebView
加载资源文件:
1 2 3 4
| pulic void loadHtmlData() { String data = ResUtils.loadRawData(context, R.raw.perm_state); weview.loadDataWithBaseURL("file:///android_res/raw/", data, "text/html", "utf-8", null); }
|
但是如果生成的 Apk 是经过混淆的话,这种方案需要在混淆文件中加入如下配置(至于为什么要这样做,暂未可知):
1 2 3 4
| -keepclassmembers class **.R$* { public static <fields>; } -keep class **.R$*
|
这种方式 perm_state.html
和 perm_state_style.css
基本在同一目录,结构清晰,但是会在最终混淆过后生成的 Apk 中保留 R.java
文件,会稍微增加 Apk 最终大小。
css 置于 assets 目录
将 css
置于 /assets
目录,即生成 /assets/perm_state_style.css
。
使用 WebView
加载资源文件:
1 2 3 4
| public void loadHtmlData() { String data = ResUtils.loadRawData(context, R.raw.perm_state); weview.loadDataWithBaseURL("file:///android_asset/", data, "text/html", "utf-8", null); }
|
这种方式 perm_state.html
和 perm_state_style.css
不在同一目录,结构不太清晰,但是因为没有混淆限制,不会在最终 Apk 中生成 R.java
文件,稍微减少 Apk 最终大小。
孰优孰劣,暂未可知。
相关源码请参考个人项目 SmsCodeExtractor
参考
File under /res/raw not accessible in Debug buildvariant