Kotlin Multiplatform for OpenHarmony 实战:为 Kodein-DI 实现依赖注入适配

社区引导(开头):欢迎加入KMP&CMP 鸿蒙社区: https://atomgit.com/CPF-KMP-CMP
摘要
本文是《为 Koin 实现依赖注入适配》的姊妹篇,聚焦 KMP&CMP 生态中另一款主流 DI 库 Kodein-DI 在 OpenHarmony 上的适配实践。文章复用与 Koin 完全相同的“三层架构”模板(语义层 / 引擎层 / 验收页),仅将语义层契约替换为 Kodein 风格:用 ArkTS 复刻 bindSingleton / bindFactory / instance() 注入关键字,实现零 @kit.* 的纯语义层;引擎层则直接复用 Koin 篇的 AbilityContext 桥。验收页通过“自增 id”直观证明 singleton 返回同一实例、factory 每次新建、instance() 能反查依赖链。文末附 ArkTS 严格模式下的踩坑小结与适配仓库地址,两篇可对读,验证同一框架适配不同 DI 库的通用性。
目录
- 一、为什么在 Koin 之后又写 Kodein-DI
- 二、版本口径(对外统一宣称)
- 三、适配思路:复用三层架构,只换语义层契约
- 四、语义层:复刻 Kodein 的 DI 契约(纯 ArkTS,零 @kit)
- 五、引擎层:与 Koin 共用的 AbilityContext 桥
- 六、验收页:用自增 id 证明 singleton / factory / instance
- 七、踩坑小结(ArkTS 严格模式)
- 八、适配产出与地址
一、为什么在 Koin 之后又写 Kodein-DI
上篇《为 Koin 实现依赖注入适配》讲了“DI 库在 OpenHarmony 上如何把 AbilityContext 桥接进图”。但 KMP 生态里 DI 并非一家独大——Kodein-DI 是另一款被广泛使用、且 API 风格与 Koin 截然不同的库:Koin 是“DSL 声明式”(module { single { } }),Kodein 则是“容器 + bind<T> with singleton/factory + instance() 注入关键字”的显式风格。
把两个 DI 库都适配一遍,价值有两层:对读者,能直观看到“同一件事(对象注册与按图取用)在鸿蒙上可以用两种写法落地”;对适配实践,能验证本系列“三层架构”模板的通用性——同一个引擎层桥(AbilityContext → PlatformContext),语义层换成 Kodein 的契约,验收页就能跑出 DI 效果。本文即沿这条线,复用与 Koin 完全相同的“展示页”基础框架,给出 Kodein 的适配代码与上屏验证。
二、版本环境
-
跨平台框架:KMP v2.2.21 / CMP v1.9.2
-
开发环境:DevEco Studio 26.0.0 Release;真机/模拟器 ROM 6.1+
本项目开发界面如下:

三、适配思路:复用三层架构,只换语义层契约
- 语义层(Semantic):用 ArkTS 复刻 Kodein 的核心契约——
DI构建器(bindSingleton/bindFactory)、DIContainer(get/instance,单例走缓存)。零@kit.*。 - 引擎层(Engine):与 Koin 篇相同的
AbilityContext桥,封装成可注入的PlatformContext(偏好存储 + 包信息)。重点说明:DI 库在鸿蒙上都需要它。 - 验收页(Verify):用
DIContainer注入多个服务,用“自增 id”证明singleton是同一实例、factory是不同实例、instance()能反查依赖。
四、语义层:复刻 Kodein 的 DI 契约(纯 ArkTS,零 @kit)
Kodein 的本质是“按 tag 存工厂、按 tag 取实例、单例走缓存”,并支持在工厂内用 instance() 反查依赖。我们用 ArkTS 等价复刻:
// Kodein.ets —— 语义层:复刻 Kodein 的 DI 契约
export interface Binding {
readonly scope: 'singleton' | 'factory';
create(di: DIContainer): Object;
}
export class DI {
private readonly bindings: Map<string, Binding> = new Map<string, Binding>();
bindSingleton(tag: string, factory: (di: DIContainer) => Object): void {
this.bindings.set(tag, { scope: 'singleton', create: factory });
}
bindFactory(tag: string, factory: (di: DIContainer) => Object): void {
this.bindings.set(tag, { scope: 'factory', create: factory });
}
get raw(): Map<string, Binding> { return this.bindings; }
}
export class DIContainer {
private readonly di: DI;
private readonly registry: Map<string, Object> = new Map<string, Object>();
constructor(di: DI) { this.di = di; }
// 对应 Kodein 的 instance() 注入关键字:在工厂内按 tag 反查依赖
instance<T>(tag: string): T { return this.get<T>(tag); }
get<T>(tag: string): T {
const b: Binding | undefined = this.di.raw.get(tag);
if (b === undefined) { throw new Error(`Kodein: 未绑定 "${tag}"`); }
if (b.scope === 'singleton') {
const cached: Object | undefined = this.registry.get(tag);
if (cached !== undefined) { return cached as T; }
const created: Object = b.create(this);
this.registry.set(tag, created);
return created as T;
}
return b.create(this) as T;
}
}
对比 Koin:Koin 的 single / factory 是 Module 上的方法,Kodein 这里用 bindSingleton / bindFactory 表达同样的语义;instance() 是 Kodein 的标志性注入关键字,我们用 DIContainer.instance(tag) 一比一对应,让“工厂内反查依赖”读起来和 Kotlin 侧 instance() 同构。
五、引擎层:与 Koin 共用的 AbilityContext 桥
Kodein 在 Android 上靠 androidContext() 注入 Context,鸿蒙这边同样用 AbilityContext。这段代码与 Koin 篇完全一致——因为“平台能力”这件事与具体 DI 库无关:
// OhosKodeinEngine.ets —— 引擎层(与 Koin 共用的桥)
import { preferences } from '@kit.ArkData';
import { bundleManager } from '@kit.AbilityKit';
import { common } from '@kit.AbilityKit';
export interface PlatformContext {
readPref(key: string): Promise<string>;
writePref(key: string, value: string): Promise<void>;
bundleName(): string;
}
export class OhosPlatformContext implements PlatformContext {
private readonly ctx: common.UIAbilityContext;
constructor(ctx: common.UIAbilityContext) { this.ctx = ctx; }
async readPref(key: string): Promise<string> {
const store = await preferences.getPreferences(this.ctx, 'kodein_demo');
return (await store.get(key, '')) as string;
}
async writePref(key: string, value: string): Promise<void> {
const store = await preferences.getPreferences(this.ctx, 'kodein_demo');
await store.put(key, value); await store.flush();
}
bundleName(): string {
const info = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT);
return info.name;
}
}
正因为桥是通用的,Kodein 篇真正要写的“差异化代码”几乎全在语义层——这也是三层架构的价值:引擎层一次写好,换库只改语义层与验收页。
六、验收页:用自增 id 证明 singleton / factory / instance
验收页建出完整 DI 图(platform 单例、greeting 单例且依赖 platform、userRepo 工厂且依赖 greeting),然后用一个模块级自增计数器给每个对象编 id,取两次比对 id 是否相同:
// KodeinDemo.ets —— 验收页(节选)
let instanceSeq: number = 0;
class Identified { readonly id: number = ++instanceSeq; }
class GreetingService extends Identified {
constructor(private readonly platform: PlatformContext) { super(); }
greet(name: string): string { return `Hello, ${name}! (bundle: ${this.platform.bundleName()}) [svc#${this.id}]`; }
}
class UserRepository extends Identified {
constructor(private readonly greeting: GreetingService) { super(); }
}
function buildGraph(ctx: common.UIAbilityContext): DIContainer {
const di = new DI();
di.bindSingleton('platform', () => new OhosPlatformContext(ctx));
di.bindSingleton('greeting', (c) => new GreetingService(c.instance<PlatformContext>('platform')));
di.bindFactory('userRepo', (c) => new UserRepository(c.instance<GreetingService>('greeting')));
return new DIContainer(di);
}
点开页面,验证结果直接上屏:


greeting 单例:取两次 id=1 / id=1 → 同一实例(单例生效)userRepo 工厂:取两次 id=2 / id=3 → 不同实例(工厂生效)welcome 输出:Hello, User! (bundle: 你的包名) [svc#1] [repo#2]
id 一致证明单例缓存生效,id 不同证明工厂每次新建,instance() 把 platform 注入进 greeting、再把 greeting 注入进 userRepo 的依赖链完整跑通——Kodein 风格的 DI 在真机/模拟器真实生效(运行截图请见文末提交要求)。
七、踩坑小结(ArkTS 严格模式)
- 构造器不能带可见性修饰符:
constructor(private readonly x)被禁,必须普通参数 + 显式this.x = x+ 在子类里super()后赋值。 - 子类构造
super()必须第一句:ArkTS 严格要求super()先于任何this访问,Identified的字段初始化在super()内完成。 - 单例缓存用
Map手动管:避免 ArkTS 对顶层可变状态的过度限制,与 Koin 同款做法。 instance()只是get()的别名:Kodein 的注入关键字在 ArkTS 侧用方法别名最干净,避免引入额外泛型魔法。AbilityContext不能在模块顶层取:必须等组件就绪后用this.getUIContext().getHostContext()传入buildGraph。
八、适配产出与地址
- 适配仓库(AtomGit):https://atomgit.com/oh-tpc/kodein
- 与 Koin 共用“三层架构”模板:语义层零
@kit.*,引擎层桥@kit.ArkData+@kit.AbilityKit;两篇可对读,验证同一框架适配不同 DI 库的通用性。
社区引导(结尾):欢迎加入KMP&CMP 鸿蒙社区: https://atomgit.com/CPF-KMP-CMP
如果你在适配过程中用到了 AI 辅助工具,推荐码道(AtomCode)专属通道:
https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwatomgit1&sourcead=dmzntgwatomgiths
更多推荐



所有评论(0)