AndroidX技术栈之Native小程序

什么是native版小程序?

蚂拉松大赛中开发者使用的小程序是传统意义上的小程序,也是通过开放平台对外的小程序,支付宝上的ofo、哈罗单车等应用也是此类小程序。一般把此类小程序称为web版小程序。一般说小程序,都是指web版小程序

native版小程序和web版小程序的关系就如同React-NativeReact的关系。native版小程序是渲染在Native-Render上的,web版小程序是渲染在WebKit上的Native-Render是基于React-Native打造的。

上图左半部分展示了web版小程序的完整研发运行流程,右半部分则是native版小程序的。无论是native版小程序还是web版小程序整体的研发流程都是一样的。

  1. 编写业务代码 —— 相同的DSL,不区分web还是native。
  2. 工具编译打包 —— 不同的打包工具,产物区分web和native。
  3. 调用前端框架 ——af-appxaf-appx-native分别是web和native的前端框架,他们基于相同的内核框架appx
  4. 不同平台渲染 —— web渲染在webkit,native渲染在native render

除了DSLAppX核心框架是相同的之外,web版小程序和native版小程序的具体实现都是不一样的。

native版小程序的优势

native版小程序的目标是打造一个兼具小程序和React-Native优势的新框架,在web版小程序基础上提供渐进增强的能力

Bang神的小程序技术方案探讨对小程序的优劣有了不错的思考。

native版小程序在技术层面的优势主要是三个层面:

  • 体验优势
  • 研发优势
  • 动态能力

这是一个已经被验证过很多次的话题了,贴个知乎链接,见仁见智。

此外,小程序的DSL意味着可以自定义标准,开发者可以打造自己的生态圈,实现组件和API的复用,在基础能力上实现抽象,降低开发、迭代产品的工作量。其中值得一提的是native版小程序的组件扩展能力,相比于web有比较大优势。

关于native版小程序的优势,蚂蚁财富之前有过一次比较全面的数据比较,蚂蚁财富小程序实战性能数据。近期工具框架都做了比较大的优化,后续还会基于新工具、新框架再做一次全面对比。

native版小程序的劣势

相比与web版小程序,native版小程序的劣势主要在于:

  • css支持的比较有限
  • 调试没有web方便(虽然native版小程序也有不错的调试能力,但是毕竟相比web的体验还是差不少的)

native版小程序的技术要点

native版小程序的开发运行流程如下,native版小程序在开发、运行期都做了大量的技术设计和优化。本文只讨论与native相关部分的工作,前端的大量技术细节可以参考小程序原理解析系列文章

设计技术要点:

  • 三端统一

开发期的技术要点:

  • 调试能力
  • 分包打包

运行期的技术要点:

  • 预加载
  • 应用隔离
  • 分包加载
  • 降级机制
  • 保活机制
  • 区块能力

三端统一

所谓三端统一,就是同一份代码在iOSAndroidWeb三个平台展示效果是一样。iOSAndroid是指使用Native Render渲染,Web是指使用Web Render渲染,其中Native Render是基于React-Native打造的,Web Render就是Webkit

三端统一是在业务实践中得出的结论。早期的财富账单等native版小程序都不是三端统一的,只能跑在Native Render上。但是随着还贷管家、工资理财等业务出现,这些业务要跑在多端,而部分客户端上并没有native版小程序SDK,而且业务稳定性需要100%保障,因此降级web的能力也同样重要。三端统一就是刚需。

三端统一是一个需要不断开发完善优化的工作,细节点非常多。

组件统一

组件主要可以分成两类。

  • 一类是基于React-Native的基础组件扩展而成,主要是view,text等比较通用的组件。

    这种就只有一种统一方式,就是定义好规范,然后前端实现就可以了。这类组件的更新不需要依赖native发版。

  • 一类是由Native基于支付宝或者聚宝现有的能力扩展而成的,比如iconmapvideo等。

    这种组件的对齐有两种方式。一种是web本身也是用的native组件,比如map,这种对齐就比较简单,大家共用底层的native组件就可以了。还有一种就是类似第一类定义好规范,web和native都按照规范来实现即可,比如icon就是类似的。一般这类组件的更新需要依赖native发版。

API统一

API的统一相对组件来说会简单一些,通过适配层复用了大部分H5的JSAPI,不能复用的就尤其是界面相关的API都参照标准自己实现。

样式统一

比较困难的是样式统一。webcss样式非常复杂强大,没有办法完全对齐webcss,因此优先统一了最为核心的数十个样式,保证基本渲染和业务需要。更多的样式统一还是需要根据业务的需求逐步完善。

因为web css用法是深入人心的,为了减少开发者上手的成本,约定了样式统一规范:一切以web css为标准

  1. web css可以直接转换成现有的native样式

    这部分的转换工作在样式传给组件之前处理。有很多的样式是通过这个方案来实现统一的,具体的可以参见web-react-native-style

  2. native没有现成的样式能实现web css对应的功能

    针对这种情况,就先用native开发对应的样式,然后需要的话再通过转换函数转换。背景色的渐变色就是这样实现的。

  3. native组件内部实现web css的功能

    有些css的功能可以通过在组件内部解析css属性,然后利用现有的组件和样式来实现。比如viewbackground-imageposition: fixed就是通过该方式实现的。该方式实现的样式只能使用特定的组件。比如前面提到的postition: fixedview组件里面实现,那么position: fixed样式就只适用于view组件。

  4. 默认值的统一

    比如flex-directionwidthcsswebnative的默认值并不是一样的,导致布局出来效果差异很大,因此通过全局定义的css统一了组件的默认样式。

调试能力

React-Native为调试能力做了大量的工作,native版小程序有幸站在巨人的肩膀上来有打造native版小程序的调试开发工具。

因为一个客户端应用可能包含多个bridge,不同于业内一个客户端一个bridge的设计,需要优化原有的调试框设计,保证弹出来的调试框是对应于当前展示的native版小程序的。

其次针对native版小程序的调试需求,增加了reloadAppreloadPage的功能,分别用来刷新整个native版小程序应用和当前页面。

另外,H5应用开发者可以直接使用hpm安装可调试包直接开发,接入成本非常低。native版小程序借鉴了webpack tappable的方案将调试相关的功能都移到了TinyDebug的开发工具库中。在伙伴打可切换环境包的时候将开发工具集成到安装包中,而完全不会影响线上包的正常逻辑。

现在native版小程序也可以通过hpm来安装最新的安装包直接调试开发了!

值得提的是Tappable机制是很好的一种解耦设计,后续框架会保证自身的简洁,将埋点、监控、权限等等处理都通过Tappable机制剥离出框架。

分包加载

native版小程序一开始并没有分包,而是将业务和框架打在一起,因为前期框架非常不稳定,兼容性不能保障,框架升级容易造成业务的各种不可用。随着框架渐渐成熟,分包加载将能解决整体打包导致的一系列问题。

  • 框架升级困难,业务需要重新打包发版,业务越多,框架收敛越慢。
  • 平台差异导致业务需要针对平台分别打包,分别发布,运维工作翻倍。
  • 冗余 jsbundle ,影响更新成功率,浪费带宽,浪费用户磁盘空间。

分包主要包含两个部分。

  • 拆分 react-native
  • 拆分 appx-native

拆分之后原有的臃肿的一个包变成了三个包。

  • ReactNative :rn框架包,内置在客户端的,跟随客户端版本迭代更新。
  • AppxNativeappx-native 框架包,不同平台不同框架包,框架包对下需要适配 ReactNative 升级带来的兼容性问题,对上需要保证接口兼容性。该框架包的升级由小程序框架组来负责,业务无需关心。
  • BizBundle :业务包,业务打包不再需要关心平台差异,可以根据自己的节奏迭代发版。

分包带来的收益是看得见的。

  • 业务包变小,甚至比 web 的包更小(不过一般为了降级会同时带一个 webjsbundle ,后面如果小程序支持线上直接访问,这个 webjsbundle 就可以不再需要了),好处众多,提升性能、提升更新成功率等等。
  • 框架包可以更快的进行迭代,而且因为有了 appx-native 这一中间层,兼容性更容易得到保障,业务不用再担心大量的 Breaking Change
  • 框架包吃掉了平台的差异,业务包不再区分 iOSAndroid 平台,业务的维护成本大大降低。
  • 框架包可以预加载,带来业务体验上的提升。

虽然好处不少,但分包也带来了包管理上的复杂度提升。在加载业务包之前,需要先将react-native包、appx-包加载完毕。react-native是内置的,一般问题不大。但是appx-native是动态下发的,客户端一般都会通过内置最新的appx-native框架包来解决这个问题,否则前几次可能就会走降级逻辑。另外就是appx-native发布带来的bug导致的影响面会大大提升,虽然我们能通过迭代来解决这个问题,但还是依赖框架测试、灰度机制以及线上监控来保障了。

应用隔离

React-Native类似的技术方案中如何管理JS执行环境是一个可以研究的技术点。

JSContext的管理是一个如何折中效率、内存、可靠性的问题。

WeexReact-Native官方使用方法都是一个客户端App,一个JS环境。这样的设定对于简单的应用是可以的,而且性能也是最好的。但如果业务很多,那业务的隔离性就没有保障。类似支付宝、聚宝这样的客户端应用,都包含几十上百个业务。不同的业务之间最好不要形成干扰,不能因为一个业务出问题导致整个app都不能使用。

native版小程序采用的方案是每个native小程序都拥有自己独立的JSContext。支付宝和蚂蚁财富本身就是由大量的microApp组成的。native版小程序参考H5App实现了自己的tinyApp,每个tinyApp实例都有自己的appId,它们都运行在自己特有的JSContext上。

因为小程序是本身是小应用的解决方案,不同的页面之间,页面与全局数据之间都存在大量的交互,小程序应用独立JSContext的设定不会影响小程序内部的交互性能。

而且任何一个业务的出错都不会影响到别的业务,任何一个业务的异常都可以由自己的降级逻辑来处理。

在使用支付宝或者蚂蚁聚宝的时候,每次都只能使用一个microApp,也就是说从框架层面限制了会且只会保留有限数量的tinyApp,内存方面的压力也不会很大。

而且传统使用React-Native的一大痛点是随着业务的迭代,jsbundle越来越大,导致加载速度和执行速度越来越慢。应用隔离保证了每个业务的jsbundle体积可控,也就保证了应用的体验。

应用隔离应该是解决JSContext问题的最佳方式了。

预加载

native版小程序启动的最大瓶颈是jsBundle的加载,jsBundle的加载耗时会极大的影响应用首屏展示速度。针对该痛点的解决方案是在拆包的基础上进行应用的预加载。虽然预加载并不能解决冷起打开慢的问题,但是根据线上数据预计有大约80%以上的业务会在预加载完成之后才被用户使用,预加载的价值还是非常大的。

预加载的实现比较简单,先合并内置的React-Native的包和当前最新的Appx-Native框架包,然后创建一个新的bridge并异步加载合并好的bundle,然后将这个bridge放到bridge池中,等待native版小程序应用来取用。小程序在取用的时候会判断bridge是够已经准备好,如果准备好了就会直接加载业务bundle,如果没有的话,就会先等待前置bundle加载完,再加载业务bundle

预加载的策略是比较动态的,有通用的策略,也有适用于业务的自定义策略。

通用的策略是在启动一个小程序应用都会在1s之后准备一个空闲的bridge放到池子中供下一个小程序应用的打开。

而业务也可以在特定的时机去执行预加载。

  • 比如框架可以在应用启动后,CPU空闲的时候去执行预加载。
  • 比如在支付宝上,在用户点击财富Tab的时候开始预加载,用户如果晚点再点击工资、还贷等native版小程序,就能享受预加载带来的红利。
  • 比如在蚂蚁财富,在应用启动的时候就启动了一个区块的小程序,1s之后,就会执行预加载。

保活机制

为了保证用户再次打开最近打开过的几个native版小程序时能有最佳的体验,将用户最近使用的5个native版小程序进行了缓存。缓存采用了FIFO的策略,当达到缓存池上限的时候或者接受到系统内存警告的时候,最先被缓存的应用将最先被释放。

为了最大化缓存的利用效率,没有对页面进行缓存,主要是对Bridge进行了缓存,因为在准备好的Bridge上创建页面并显示耗时是非常短的。

保活机制的引入导致应用管理的复杂度提升到另外一个级别。应用管理的最难点是保证应用栈的正确性,应用栈决定了应用在后退、跳转、调试等诸多方面的功能是否正常。native版小程序付出了很大的努力来保证应用栈的准确性。除了应用栈,还给应用增加了引用计数的功能,来解决同一个native版小程序多开的问题。

降级机制

降级能力是指当native版小程序加载或者运行出错的时候,能够自动降级成web版小程序显示。一开始做native版小程序并没有太多的考虑降级问题,尤其是在蚂蚁财富上面,除非native闪退,需要降级的场景还是比较少的。
但是随着业务发展发现降级是业务的刚需。

  • 对于业务来说,稳定是第一位的,然后才是性能和体验。
  • 支付宝上面Android的native小程序SDK是通过推送bundle的方式来做的,而iOS因为包大小的问题也暂时没有上线。降级能力降级的前提是检测错误,检测SDK没有准备好、bundle资源加载错误、js执行错误以及其他框架错误等降级场景。

其次是降级的方案。
因为native版小程序的降级是应用级别的降级,如何让用户无感知的降级也是一件挺有意思的事情。因为降级的检测是在启动了native版小程序之后才能感知的,如果降级采用退出该应用再重新新建一个web版小程序的话,用户是可以看到一个退出再进来的过程的。因此采用的方案是壳还是native版小程序的壳,但是使用web版小程序的内核,在所有回调的地方,返回web版小程序的相关信息,从而使用web版小程序伪造了一个native版小程序。

因为不可能让业务为了适配降级专门投入人力去重新开发一个web版的小程序。因此降级的前提是三端统一,这一部分才是降级最大的工作量。

区块能力

财富早期在实践React-Native的时候是用来解决动态区块问题的,小程序区块化其实一直是的目标。native小程序SDK已经拆分出了PageAppWidgetApp,前者是native版小程序,后者就是区块应用,专门用来做动态区块化的。

现在虽然还只是简单的复用了native版小程序的能力,但是已经拥有了不错的效果。然而区块的场景和native版小程序毕竟是有所不同的,很多native版小程序的设定对区块来说都是不太适合的,比如native版小程序对于navigationBar的操作,再比如native版小程序的包管理机制,再比如打包的方式等等。区块需要更实时地更新,更轻量运维和更方便的开发调试。

区块能力并非native版小程序的出发点,而是其衍生的非常重要的一块能力,这一块后续会另外成文介绍。

results matching ""

    No results matching ""