HAR 里放单例,两个 HAP 各拿各的那一份
抽了一个公共工具包,里面写了一个 ConfigManager 单例,登录态、缓存、全局开关都挂在上头。Entry HAP 里初始化完,Feature HAP 里读出来是空的。你以为是初始化时机的问题,把 onCreate 顺序调了又调,还是空。
根因在 HAR 的打包方式:HAR 是编译期静态共享包,每个引用它的 HAP 或 HSP 都会各自编译进去一份代码副本。Entry 里 new 出来的单例,和 Feature 里 new 出来的单例,物理上就是两个不同模块作用域里的两份实例。HAR 里定义对外单例,在多个使用方之间无法实现进程级单例。
要共享单例,单例本身必须放在 HSP 里
HSP 是运行期共享包,进程内只保留一份,单例、缓存、注册表这类需要跨模块共享的状态才应该放进去。HAR 留给工具函数、常量、轻量 UI 组件、纯数据模型这些无状态或者每次调用独立的东西。
还有一个编译期红线:HAR 不能依赖 HSP。报错原文就是 HAR cannot depend on HSP。如果某个 HAR 需要 HSP 里的能力,正确做法是把这段逻辑上移到 HSP,或者让 HAP 同时依赖两者,由 HAP 层做组装,不要让 HAR 反向咬住 HSP。
循环依赖只能靠单向 DAG 根治
A 依赖 B、B 又依赖 A,编译或加载顺序无法确定,直接报错。临时改 dependencies 顺序没有用,依赖图必须是有向无环图。
可行的拆法是把接口、类型、常量抽到一个最底层的 common HSP,各业务模块实现具体类但互不 import,在 entry 里把实现注册到 common 的注册点,页面跳转走路由名字符串而不是直接 import 对方页面组件。
typescript
// common HSP
export interface IOrderService {
openOrder(orderId: string): void
}
let impl: IOrderService | undefined
export function registerOrderService(service: IOrderService): void {
impl = service
}
export function getOrderService(): IOrderService | undefined {
return impl
}
registerOrderService 在 EntryAbility.onCreate 里调用一次,其他模块只依赖 common 的接口。impl 放在 HSP 里,进程内只有一份,注册和读取指向同一个变量。如果 common 做成 HAR,Entry 注册进去的实现和 Feature 读到的 impl 又是两份副本,单例问题原样复现。
更多推荐

所有评论(0)