最近做了安卓app前端资源本地化的工作,下面说明一下具体实现的一些点。
概览
最近做了安卓app前端资源本地化的工作,下面说明一下具体实现的一些点。
概览
如图所示,原本我们的app基本上是一个纯套壳的app,单webview嵌入了一个线上的h5页面,App提供视图渲染和功能拓展(拍照,定位等),只要去更新线上的前端代码就可以更新当前app了,但是这样会有一个弊端,杀掉程序后,每次进入app都会重新向server发送请求来加载资源,尽管目前我们的前端包不算大,在网速一般的时候不可避免的会出现白屏的情况。如果内嵌的前端资源有大量图片,这种情况会更加糟糕。
改造后,前端资源会缓存在本地,webview的地址也指向了本地的server,这样就可以解决上面说的痛点,但与此同时又带来了新的问题。下面梳理一下这次优化所碰到的坑。
坑点
我们所使用的跨平台框架是capacitorjs,官方文档在这里。
- 网络请求,之前前端资源和服务器上的接口属于同域,所以不会出现跨域问题,可是现在页面实际运行的地址是localhost,那么要么后端去做一下跨域处理,这显然不合理。所以最好的方式是走安卓端请求接口的方式,安卓请求接口是比http更底层的tcp,不存在跨域。
在api的http部分,给了一些示例,所以我们要做的就是把目前的访问接口方式批量改成这种吗?早先我也是这么认为的,我们用的ajax请求库是axios,那么动手将两种风格统一,手动去实现拦截器等功能也是一个不小的工作。
后面发现,只要引入这个库,请求似乎都走了native,不难想象出有针对xhr做全局拦截处理的代码。示例是这样写的
import { CapacitorHttp } from '@capacitor/core';
然而如果想当然以为这一部分的代码就在这里,那就错了,翻看源码后得知这一部分的逻辑在native-bridge.js里
所以如果想让网络请求正常工作,需要安卓判断是否有CapacitorHttp相关的处理(低版本的capacitor并没有这些插件),有了插件也要检查native-bridge是否有全局劫持xhr的代码。
即使做了这些,有些特殊的请求还是要手动兼容,目前发现上传文件接口就会出现(本质是multipart和json两种数据类型的原因)。
热更新,之前说了直接使用线上地址的好处是随时发版,内容可变,那如果做本地化也想要这样的效果需要怎么做呢,这个capacitor实在是过于小众,比较麻烦。最终我找到的方案就是cap-go的插件,具体使用的方法文档说明的还算清晰,不再赘述。需要注意的是压缩包不能包括根目录(选取多文件压缩多项)
自动化脚本问题,我们没有一个完整的CI工具,所以之前上传前端资源使用的是我基于nodejs写的脚本,根据不同环境打包,然后通过sftp协议传到服务端,实际写过nodejs脚本的人应该知道,设计之初的nodejs包含大量的回调语法,和现在promise风格的API是格格不入的。所以这次我基于zxjs进行了优化,整体流程更加直观易懂。
脚本上传文件的时候,又发现了新的问题,swagger和postman都能通,包括本地起的server模拟文件服务也能成功,但是在线上环境就是无法直接后台访问接口上传,关键时候使用了一下apiFox,明白了其中缺失的部分。
axios({
url:`${baseUrlMap[env]}/management/web/uploadSource`,
method:'post',
data:formData,
headers:formData.getHeaders() // 关键语句
})
结论
事实上hybrid App在我们日常生活中存在很多,拿支付宝举例,有时需要从应用市场重新下载,有时只需要在页面上点击一下更新资源,这个就是app热更新。只要知道原理,调试的时候就不会毫无头绪。