Android 混合开发中加载本地资源那点事

1,633 阅读3分钟

我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!

周末跟许久未见的老王(前同事)约了个饭。我临时有事,到那都晚上9点了,很是惭愧。席间聊到公司在做“套壳”App,问我如何加载自定义自体。为了弥补自己的迟到,在整理 demo 时,顺便记录下这个过程。

刚好我最近在公司负责自研小程序框架的重构,所以这事简单啊,就调一个API。便答应回家把关键字发给他。

 // Android加载自定义字体
 // 1. 配置WebViewClient
 webView.setWebViewClient(webViewClient);
 // 2. 拦截请求
 webViewClient.shouldInterceptRequest()

其核心思想就是:拦截。

一、原理

由webView加载一个页面(loadUrl),如果被加载的html放在 assets 目录下,那么使用 file:///android_asset/xxx.html 形式加载,如果是远端地址,则按浏览器加载网页逻辑填写url即可。 pic1.png 对于加载本地资源这种情况,我们要在发出的请求,离开App之前,给它拦下来。比如:要加载本地字体,那就要识别这次请求,在 shouldInterceptRequest 中根据约定规则,去本地找字体资源,并构建出请求的响应体,也就是上图中的 response。 pic2.png

二、实战

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" alt="">
 </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 端代码

  1. 在布局文件中增加 WebView,这个简单,不贴代码了。

  2. 设置 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);
       }
     });
    
    1. 拦截 url 中包含 font 关键字的请求
    2. 打开 assets 中的字体
    3. 构造响应体 WebResourceResponse
  3. 加载url

     webView.loadUrl("file:///android_asset/index.html");
    

三、后续

今天周一,老王问我调哪个方法,我便给它两个方法: image-20220228224948952.png 过一会,他把 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 相关的,感兴趣的可以读一下。