框架设计的重新思考与 HarmonyOS Next 实践
在鸿蒙化过程中,我们不仅对框架设计进行了深刻反思,更通过实际应用规避了安卓系统中的历史包袱与遗留问题。高内聚、低耦合等设计原则虽然耳熟能详,但真正落地时依然充满挑战。因此,本文将聚焦实际问题,结合鸿蒙化的设计思考,带大家一探究竟。
整体架构:组件化
第一版架构图:组件化设计
架构特点及问题分析:
- 基础组件接口暴露过多:
- 问题1:配置接口与使用接口统一暴露,容易导致配置接口被多次调用,出现重复初始化问题。
- 问题2:暴露过多的接口,尤其第三方团队维护的组件(如分享库),实际业务中使用的接口寥寥无几,增加了单元测试的难度与成本。
- 接口与实现高度耦合:
- 问题1:以实现角度设计接口,增加接口的使用成本,开发者难以迅速上手。
- 问题2:接口变更成本过低,难以控制变更规则,导致迭代时单元测试无法复用,测试成本大幅上升。
解决方案:提取服务抽象层,严格区分业务接口与实现。
第二架构图:组件化 + 服务抽象层
服务设计规范:
服务规范1:只暴露必要接口,屏蔽不必要的配置接口
以网络组件库为例,我们将服务接口控制在业务需求范围内,避免暴露配置接口或未使用的接口:
服务规范2:聚合接口,提升业务使用便捷性
为了简化业务调用,我们在接口层充当业务的BFF层,聚合接口。例如,使用Proxy实现依赖注入,确保接口调用与直接使用组件库保持一致,避免了依赖注解或容器管理带来的繁琐操作。
1、使用Proxy来实现依赖注入,而不采用注解或依赖管理容器来提供依赖入,保障与直接组件库的使用方式的一致,在Android中,我们使用的是依赖注入容器管理类,导致每次使用前,都需要先获取相应的服务才能使用
2、对数据做聚合,减少Api的调用次数。例如,登录库中获取PPU、UserId、Ticket票据分别属于不同模块,但业务开发者不需要关注这些细节,接口层将这些细节屏蔽。
服务规范3:异步化操作,解决性能瓶颈
对于耗时操作(如IO、网络请求等),我们采用Promise异步调用。在安卓中,像SharedPreferences、MMKV等库大多是同步方法,虽然简便,但在大规模应用中,容易出现卡顿现象。通过异步化,避免主线程阻塞。
ArtTS语言提供了Promise异步调用,以及await同步写法写异步调用过程,在鸿蒙化的服务中,涉及到IO等操作的Api都应返回Promise,用白屏或Loading来解决卡顿问题
当不支持异步时,我们通过状态回调解决问题。例如,在Web设置Cookie时,为了应对异步返回,我们需在onLoadIntercept()方法中拦截URL加载,确保Cookie设置完成后再继续加载URL。
服务规范4:Impl实现库的依赖模块延迟初始化,提升性能
为了减少冷启动时间,我们采用延时初始化策略,避免依赖模块在构造函数中初始化,确保逻辑不受始化顺序影响。
服务规范5:测试覆盖率要求
服务库的单元测试必须覆盖100%的使用接口,由于不与具体实现库耦合,将会大幅提升测试Case使用率
服务规范6:抽象层变动需严格审核
由于服务抽象层的变动会影响到所有业务,必须经过严格审核。实现层的迭代则通过完善的测试用例来保障质量,降低线上风险。
业务功能:模块化拆分
传统的业务模块划分以BG为单位,颗粒度较大,随着业务规模增大,模块变得臃肿。在鸿蒙化过程中,我们进一步细化拆分,按功能模块进行划分,带来了以下好处:
- 便于分析包大小。
- 不同场景下实现按需加载或替换,尤其适合厂商定制需求。
- 满足元服务对包大小的要求,实现分包加载。
View(视图层):MVVM架构
鸿蒙的双向数据绑定大大简化了View层与ViewModel层的通信。构建MVVM框架时,我们仅需关注以下关键点:
- ViewModel设计原则:按相关性创建ViewModel,确保相关属性聚合在同一ViewModel中,而非散落于View层。
Model(数据层):单一数据源,简版Repository架构
我们采用单一数据源的设计来解耦数据层,以Model层为例,所有数据操作均通过单一接口调用,避免多源数据导致的复杂性。
以Model举例:
在实现库中也是如此,如下图所示,简化了实现逻辑,确保各模块的独立性与可维护性。