我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!
周末跟许久未见的老王(前同事)约了个饭。我临时有事,到那都晚上9点了,很是惭愧。席间聊到公司在做“套壳”App,问我如何加载自定义自体。为了弥补自己的迟到,在整理 demo 时,顺便记录下这个过程。
刚好我最近在公司负责自研小程序框架的重构,所以这事简单啊,就调一个API。便答应回家把关键字发给他。
// Android加载自定义字体
// 1. 配置WebViewClient
webView.setWebViewClient(webViewClient);
// 2. 拦截请求
webViewClient.shouldInterceptRequest()
复制代码
其核心思想就是:拦截。
一、原理
由webView加载一个页面(loadUrl),如果被加载的html放在 assets 目录下,那么使用 file:///android_asset/xxx.html
形式加载,如果是远端地址,则按浏览器加载网页逻辑填写url即可。
对于加载本地资源这种情况,我们要在发出的请求,离开App之前,给它拦下来。比如:要加载本地字体,那就要识别这次请求,在 shouldInterceptRequest
中根据约定规则,去本地找字体资源,并构建出请求的响应体,也就是上图中的 response。
二、实战
2.1 前端代码
首先看下前端代码:
index.html
<div class="app">
<div>hello world</div>
<div>新年快乐</div>
<img src="https://img.tukuppt.com/bg_grid/01/03/50/Ljquu0ZtJN.jpg!/fh/350" >
</div>
复制代码
外层 div 设置 class选择器为 app,app内并排3个元素。这里关键在css:
<style>
@font-face {
font-family: HuaGuangGangTieZhiHei;
src: url('http://font/HuaGuangGangTieZhiHei-KeBianTi-2.ttf');
}
.app {
font: 63px HuaGuangGangTieZhiHei;
}
</style>
复制代码
这里使用 src 从服务器加载自定义字体。在类选择器 app 中使用该字体。注意这里的 http://font 并非真实的url,仅为了方便我们拦截该请求。
PS:字体的自定义url为 http://font,如果要加载本地图片,可以设置为 http://image,当然也可以统一host,这个根据自己的业务设定即可。
2.2 Android 端代码
-
在布局文件中增加 WebView,这个简单,不贴代码了。
-
设置 WebViewClient ,并重写 shouldInterceptRequest
webView.setWebViewClient(new WebViewClient() { @Nullable @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { Uri url = request.getUrl(); Log.e("MainActivity", "shouldInterceptRequest " + url.toString()); if (customFont && url.toString().contains("font")) { // 1 try { InputStream inputStream = getAssets().open("HuaGuangGangTieZhiHei-KeBianTi-2.ttf"); // 2 Map<String, String> responseHeaders = new HashMap<String, String>(); responseHeaders.put("Access-Control-Allow-Origin", "*"); return new WebResourceResponse("font/ttf", "utf-8", 200, "ok", responseHeaders, inputStream); // 3 } catch (IOException e) { e.printStackTrace(); } } return super.shouldInterceptRequest(view, request); } }); 复制代码
- 拦截 url 中包含 font 关键字的请求
- 打开 assets 中的字体
- 构造响应体 WebResourceResponse
-
加载url
webView.loadUrl("file:///android_asset/index.html"); 复制代码
三、后续
今天周一,老王问我调哪个方法,我便给它两个方法:
过一会,他把 MainActivity.java 发给我了,核心代码如下:
wb.loadUrl("http://localhost/web");
wb.setWebViewClient(new WebViewClient() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(String.valueOf(request.getUrl()));
return true;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
if (!wb.getSettings().getLoadsImagesAutomatically()) {
wb.getSettings().setLoadsImagesAutomatically(true);
}
}
});
复制代码
对比实战部分,这段代码是无法加载自定义字体的。虽然重写了 shouldOverrideUrlLoading 方法,但并没有加载 Android 端字体。
四、总结
把远程常用资源放到本地加载,是解决 h5 白屏的关键部分。不管是套壳App,还是 hybrid App,它是个系统工程。想要开发出一套完整可用的框架,需要前端到Android端的知识,尤其是网络请求,这是绕不开的。
ps: Capacitor Android 源码浅析 也是与 Hybrid App 相关的,感兴趣的可以读一下。