大家好,我是熊猫钓鱼!欢迎大家和我一起探讨技术。希望您能点赞关注,谢谢!

在这里插入图片描述

摘要

本文是「OpenHarmony 鸿蒙化三方库适配」延续「纯逻辑等价复刻」路线——在已交付 Decompose(组件化导航)、Essenty(生命周期/状态保持)、MVIKotlin(单向数据流)、Reaktive(响应式原语)之后,补齐「依赖注入」这一层:evant 开源的 kotlin-inject。它用编译期代码生成把 @Inject / @Component / @Module / @Provides / @Singleton / @Named 翻译成依赖图,让 KMP 业务零样板拿到注入好的实例。本文用纯 ArkTS 等价复刻同一套语义:Module(绑定集合)、Binding(工厂 + 作用域)、DiComponent(依赖图 + 解析 + 组件级单例缓存)、qualifier(字符串),不引入任何 @kit.*;并在 InjectDemo.ets 用四个按钮演示「构造注入 / 单例复用 / @Named qualifier 区分 / factory 与 singleton 引用差异」。

适配上属于「等价复刻」路线,且连引擎层都不需要,只有「语义层 + 验收页」两层——系统里没有任何东西叫 Component 或 Provider,全部用纯 ArkTS 还原。全文总结了 ArkTS 适配踩的 6 个坑(字段初始化调用方法、组件级单例语义、泛型 + Map 值类型、构造注入反向解析、=== 比较实例、静态字段自增),并与系列其它 8 个适配做了横向对比。其中关键的 ArkTS 命名细节是:依赖图类名取 DiComponent 以避开 ArkUI 的 @Component 装饰器冲突。

本适配基于 HarmonyOS SDK 6.0.0(20) + KMP&CMP 鸿蒙社区工具链 v1.1.0(Kotlin 2.2.21 / CMP 1.9.2)开发,assembleHap 编译 BUILD SUCCESSFUL、零 ArkTS error,纯逻辑零平台依赖,模拟器即可演示、无需任何权限或硬件。


目录

  • 一、为什么要适配 kotlin-inject
    • 中大型 KMP 应用的依赖网;与 Dagger/Hilt 同源、编译期生成、零运行时依赖
    • 适配目标:把「依赖图语义」还原成 ArkTS(@Module→Module.bind / @Inject→工厂反向 get / @Singleton→scope / @Named→qualifier)
  • 二、路线取舍:等价复刻,且只有两层
    • 系列两条路线回顾(真接口真实现 / 等价复刻)
    • 为何不复用上游 Kotlin + KSP 源码
    • 核心类型一览(Module / Binding / DiComponent / Scope / qualifier)
  • 三、语义层:把依赖图契约画出来(不碰任何 @kit.*)
    • Module:绑定集合(bindSingleton / bindFactory / include)
    • Binding:非泛型绑定载体(Object 承载,对外 as T 还原)
    • DiComponent:依赖图(解析 get + 组件级单例缓存)
    • 构造函数注入如何表达(工厂反向 component.get,对应 @Inject constructor)
  • 四、验收页:四个按钮讲清 DI 三件事
    • 解析 UserService(构造注入验证)
    • 检查单例复用(@Singleton)
    • 检查 qualifier 区分(@Named)
    • 检查 factory vs singleton(引用差异)
    • 运行效果描述
  • 五、开发调试
    • 字段初始化调用方法、组件级单例语义、泛型+Map 值类型、构造注入反向解析、=== 比较、静态字段自增
  • 六、版本与运行环境
    • 适配平台、工具链、IDE、编译验证结果
  • 小结
    • 三层架构 + 等价复刻在纯逻辑库上的高效性;arkivanov 生态底座愈发完整
    • 社区引导语与 AtomCode 专属邀请链接

一、为什么要适配 kotlin-inject

写中大型 KMP 应用,依赖关系会迅速变成一张网:页面依赖 ViewModel,ViewModel 依赖 Repository,Repository 依赖 DataSource 与 Logger……手动 new 既臃肿又难测试。

kotlin-inject 是 evant 开源的 Kotlin 依赖注入库,思路和 Dagger/Hilt 一脉相承,但编译期生成而非运行时反射,无 KAPT 性能坑、零运行时依赖,特别适合 KMP。

在 OpenHarmony 上,ArkTS 没有注解处理器(或很难接 kotlin-inject 的 KSP 生成),但我们适配的目标很明确:

把 kotlin-inject 的「依赖图语义」原样还原成 ArkTS——@Module + @Provides 变成 Module.bindXXX,@Inject constructor(...) 变成工厂函数反向 component.get(...),@Singleton / @Named 变成 scope 与 qualifier 字符串。
在这里插入图片描述

解析路径:get(userService) → 工厂向 DiComponent 反解 logger → 返回注入好依赖的 UserService 实例

二、路线取舍:等价复刻,且只有两层

和 MVIKotlin / Reaktive 一样,kotlin-inject 属于「等价复刻」路线:系统里没有任何东西叫 Component 或 Provider,所以连引擎层都不需要,只有「语义层 + 验收页」两层。

为什么不复用上游 Kotlin 源码?上游是 Kotlin + KSP 代码生成,要在鸿蒙跑需要 ohosArm64 目标与一整套注解处理流水线,成本不可控;而 kotlin-inject 的「依赖图语义」本身就是几百行纯逻辑(绑定收集、按 qualifier 解析、组件级缓存),复刻比编译上游划算得多,还能随手丢进 Node 做离线单测。

本适配实现的核心类型:Module(绑定集合)、Provider(工厂 + 作用域)、DiComponent(依赖图 + 解析 + 单例缓存)、Scope(singleton/factory)、qualifier(字符串)。

在这里插入图片描述

三、语义层:把依赖图契约画出来(不碰任何 @kit.*)

核心文件 Inject.ets 定义了一组角色,一一对应 kotlin-inject。注意:依赖图类名取 DiComponent(而非 Component),是为了避开 ArkUI 的 @Component 装饰器命名冲突——这是 ArkTS 适配里一个容易踩、但很关键的命名细节。

在这里插入图片描述

Module —— 绑定集合(对应 @Module + @Provides)

bind(qualifier, factory, scope) 是唯一的注册入口,返回新 Module(链式组合)。两个便捷方法:bindSingleton(对应 @Singleton @Provides)、bindFactory(每次新建)。include(other) 把另一个 Module 的绑定并入,对应 @Component(modules = [A, B])。

Provider<T> —— 可被解析的绑定

持有「工厂函数 + 作用域」。关键设计:Provider 本身不缓存实例,缓存决策交给 Component——这样才能保证「单例是组件级单例,而非模块级单例」。工厂函数类型是 (component: DiComponent) => T,正是它让「构造函数注入」得以表达。

DiComponent —— 依赖图(对应 @Component 生成的实现类)

get<T>(qualifier) 是唯一的消费入口:查 Module 里有没有这个 qualifier 的绑定,没有就抛「未找到绑定」;有则看作用域——singleton 且缓存命中就返回同一对象,否则 produce 新建并(若是 singleton)写入 cache。has / registered 用于自检。

构造函数注入如何表达(对应 @Inject constructor(…))

原库里 @Inject constructor(logger: ILogger) 由 KSP 生成「先解析 logger 再 new」的代码。我们在 bindFactory('userService', c => new UserService(c.get('logger'))) 里手动写出同样的逻辑:工厂函数 c 就是「生成的构造代码」,它向 DiComponent 反向解析依赖,于是 UserService 拿到的 logger 是依赖图自动注入的。

四、验收页:四个按钮讲清 DI 三件事

InjectDemo.ets 用四个按钮把「构造注入 / 单例 / qualifier / factory」全部点亮:

  • 解析 UserService:component.get<UserService>('userService')。日志打印 UserService 已注入 Console 日志器;users=Alice,Bob,Carol——证明 UserService 的 ILogger 依赖由依赖图自动注入,无需手动 new ConsoleLogger()。
  • 检查单例复用:连续两次 get('logger'),用 === 比较。日志打印 两次 get(logger) 同一引用?是(@Singleton 生效)——证明组件级单例缓存生效。
  • 检查 qualifier 区分:get('logger') 与 get('logger:file') 拿到不同实现。日志打印 default.tag=Console file.tag=File 两者不同(@Named 生效)——证明 qualifier 成功区分同名类型。
  • 检查 factory vs singleton:两次 get('counter') 拿到不同 id。日志打印 两次 get(counter) id=1 / 2 不同实例(factory 每次新建)——证明非单例绑定每次新建。

跑起来:进页自动打印「依赖图已构建,注册绑定:logger、logger:file、userService、counter」;依次点四个按钮,事件日志逐条验证 DI 三件事。整条依赖注入闭环一目了然。

在这里插入图片描述

五、开发调试

我在Dev Eco 26最新环境下,完成本项目的代码开发界面如下:
在这里插入图片描述
项目编译完成:
在这里插入图片描述
启动运行,实践测试功能完成情况:
在这里插入图片描述

解析UserService(验证构造注入)如下,成功完成!
在这里插入图片描述

下面我们检查单例复用,@Singleton,实测如下:

在这里插入图片描述
OK,已生效!
项目下面我们检查qualifier区分(@Named)
在这里插入图片描述
也成功了!
最后:检查factory vs singleton,两种模式的引用差异如下:
在这里插入图片描述
以上,全部适配测试完成!

说一下在ArkTS 适配踩的坑:

  1. 字段初始化调用方法:ArkTS 对 struct 字段初始化器 = this.buildX() 有限制,改用 private component: Component = InjectDemo.createComponent() 静态工厂,避免 this 未初始化问题。
  2. 组件级单例语义:第一版把缓存放在 Provider 上,会导致「跨 Component 共享单例」。修正为缓存放在 Component.cache,严格组件级,符合 kotlin-inject「单例存活于 Component 实例」的语义。
  3. 泛型 + Map 值类型:Module.bindings 用 Map<string, Provider<unknown>> 存异构绑定,get<T> 再 as T 还原。ArkTS 支持泛型方法,但 Map 值必须统一为 unknown 才能混装不同类型 Provider。
  4. 构造函数注入的「反向解析」:ArkTS 没有真注解,工厂函数必须显式 c.get(...) 写出依赖顺序;若漏写某个依赖,运行时才抛「未找到绑定」,所以验收页用 registered 先打印全部绑定自检。
  5. === 比较实例:单例/工厂验证依赖引用同一性,ArkTS 引用类型 === 语义与 JS 一致,可直接用于断言。
  6. 静态字段自增:Counter.seq 用 static 字段自增生成 id,ArkTS 支持 static 字段,但必须初始化(= 0)否则报状态可变性错误。

六、版本与运行环境

  • 适配目标平台:HarmonyOS SDK 6.0.0(20)(API 20)
  • 工具链:KMP&CMP 鸿蒙社区工具链 v1.1.0(Kotlin 2.2.21 / CMP 1.9.2)
  • IDE:DevEco Studio 26.0.0 Release
  • 编译验证:assembleHap BUILD SUCCESSFUL,零 ArkTS error

小结

在我看来,本次kotlin-inject 的适配再次验证了「三层架构 + 等价复刻」在纯逻辑类 KMP 库上的高效:几百行 ArkTS 把依赖注入(Module/Provider/Component、构造注入、@Singleton、@Named)完整还原,零平台依赖、模拟器即跑、可离线单测。

其实呢,配合 Decompose / Essenty / MVIKotlin / Reaktive,arkivanov 生态的「导航 + 生命周期 + 数据流 + 响应式 + 注入」底座在 OpenHarmony 上愈发完整。

本文完,谢谢大家阅读!欢迎点赞关注我!

欢迎加入 KMP&CMP 鸿蒙社区:https://atomgit.com/CPF-KMP-CMP
AtomCode 专属邀请链接:
https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwatomgit1&sourcead=dmzntgwatomgiths

Logo

一站式 AI 云服务平台

更多推荐