在鸿蒙HarmonyOS原生应用开发中随着业务复杂度的提升组件间的依赖关系会变得错综复杂。传统的硬编码依赖方式会导致代码耦合度高、难以测试和维护。依赖注入Dependency Injection, DI通过控制反转IoC机制将对象的创建和依赖关系的管理交由专门的容器来处理是解决这些问题的最佳实践。在鸿蒙 ArkTS 中我们可以利用装饰器Decorators和反射Reflect机制实现一个轻量级的依赖注入框架。一、 核心概念与架构设计一个基础的 DI 框架主要包含三个核心部分服务标识Service用于标记哪些类是“可被注入的服务”。注入标识Inject用于标记哪些属性需要由容器自动注入。IoC 容器Container负责管理服务的注册、生命周期如单例、瞬时以及自动解析依赖关系。一个“用户资料展示”的实战场景1. IoC 容器Container全局的“服务管家”容器是整个 DI 框架的核心负责记录“谁提供了什么服务”以及“如何创建服务”。class DIContainer { private services: Mapstring, any new Map(); // 1. 注册服务将接口标识与具体的类或实例绑定 register(identifier: string, service: any): void { this.services.set(identifier, service); } // 2. 解析服务根据标识获取具体的服务实例 resolveT(identifier: string): T { const service this.services.get(identifier); if (!service) { throw new Error(Service [${identifier}] 未注册); } return service as T; } } // 导出一个全局单例容器供整个应用使用 export const container new DIContainer();2. 服务标识Service标记“可被注入的能力”通常会定义一个接口或抽象类作为标识并让具体的服务类去实现它。// 1. 定义服务标识接口 interface IStorageService { getData(key: string): string; saveData(key: string, value: string): void; } // 2. 具体的服务实现 class LocalStorageService implements IStorageService { getData(key: string): string { return 从本地获取的 ${key}; } saveData(key: string, value: string): void { console.log(已保存到本地: ${key} ${value}); } } // 3. 在应用启动时向容器注册服务 container.registerIStorageService(IStorageService, new LocalStorageService());3. 注入标识Inject在业务层“自动获取依赖”在 ViewModel 或 UI 组件中我们不再手动new具体的服务类而是通过容器获取。class UserViewModel { // 传统方式高耦合 // private storage new LocalStorageService(); // DI 方式低耦合通过容器解析获取 private storage: IStorageService container.resolveIStorageService(IStorageService); loadUserProfile() { // 业务层只关心接口不关心底层是本地存储还是网络存储 return this.storage.getData(user_profile); } }完整串联在鸿蒙 View 层中的使用Component struct UserPage { State profileInfo: string 加载中...; private viewModel: UserViewModel new UserViewModel(); aboutToAppear() { // 调用 ViewModel 的方法ViewModel 内部自动使用了注入的 Storage 服务 this.profileInfo this.viewModel.loadUserProfile(); } build() { Column({ space: 20 }) { Text(this.profileInfo).fontSize(20) Button(切换为网络存储) .onClick(() { // 动态替换服务实现依赖倒置的威力 container.registerIStorageService(IStorageService, new NetworkStorageService()); this.profileInfo this.viewModel.loadUserProfile(); }) } } }二、 轻量级 DI 框架代码实现// 1. 服务标识装饰器将类标记为服务并可选地指定一个唯一标识符 function Service(identifier?: string): ClassDecorator { return function (target: any) { Reflect.defineMetadata(di:service, true, target); if (identifier) { Reflect.defineMetadata(di:identifier, identifier, target); } }; } // 2. 注入装饰器标记需要注入的属性并记录其依赖的类型或标识符 function Inject(identifier?: string): PropertyDecorator { return function (target: any, propertyKey: string | symbol) { const serviceIdentifier identifier || Reflect.getMetadata(design:type, target, propertyKey); Reflect.defineMetadata(di:inject, serviceIdentifier, target, propertyKey); }; } // 3. IoC 容器类负责注册和解析服务 class DIContainer { private static instance: DIContainer; private services: Mapstring, any new Map(); // 获取全局单例容器 static getInstance(): DIContainer { if (!DIContainer.instance) { DIContainer.instance new DIContainer(); } return DIContainer.instance; } // 注册服务实例 registerT(identifier: string, service: T): void { this.services.set(identifier, service); } // 解析并获取服务实例 resolveT(identifier: string): T { const service this.services.get(identifier); if (!service) { throw new Error(Service ${identifier} not found); } return service; } // 自动注册扫描带有 Service 装饰器的类并实例化 autoRegister(constructor: any): void { const isService Reflect.getMetadata(di:service, constructor); if (isService) { const identifier Reflect.getMetadata(di:identifier, constructor) || constructor.name; this.register(identifier, new constructor()); } } }三、 业务层实战示例结合之前的 MVVM 架构DI 框架可以完美地应用于服务层和 ViewModel 层// 1. 定义服务层使用 Service 标记 Service(UserService) class UserService { private users: Mapstring, any new Map(); addUser(user: any): void { this.users.set(user.id, user); } getUser(id: string): any { return this.users.get(id); } } // 2. 定义 ViewModel使用 Inject 注入依赖 Service(UserViewModel) class UserViewModel { // 容器会自动将 UserService 注入到该属性中 Inject(UserService) private userService!: UserService; loadUser(id: string) { return this.userService.getUser(id); } } // 3. 在 UI 组件中使用 Component struct UserProfile { Inject(UserService) private userService!: UserService; State userInfo: any null; aboutToAppear(): void { // 从容器中获取服务并加载数据 this.userInfo this.userService.getUser(user123); } build() { Column() { Text(this.userInfo ? 用户名: ${this.userInfo.name} : 加载中...) } } }四、 生命周期与最佳实践在实际的鸿蒙工程中DI 框架的使用需要遵循以下规范分层架构设计将 UI 组件、ViewModel、数据服务严格分层通过 DI 容器进行连接确保各层职责单一。生命周期管理策略Singleton单例适用于全局配置、数据库连接、工具类。容器内缓存实例整个应用生命周期内共享。Transient瞬时适用于每次请求都需要新实例的场景。每次调用resolve时创建新实例。Scoped作用域适用于请求上下文或特定页面。在作用域内共享实例页面销毁时自动释放资源。配置集中管理可以将 API 基础 URL、超时时间等配置封装为AppConfig服务通过 DI 注入到网络请求服务中避免硬编码。延迟注入与缓存避免在应用启动时如Application.onCreate()一次性注入所有依赖。采用懒加载策略并对频繁使用的依赖进行缓存以提升应用启动性能。五、 进阶扩展跨平台与模块化 DI 方案如果你的鸿蒙项目是基于Flutter for OpenHarmony开发或者需要处理多 HAPFeature模块的复杂工程可以考虑使用成熟的第三方 DI 库weaver一个极致轻量的 DI 和服务发现框架。它提出了“注册Register- 注入Inject”模型支持局部作用域容器weaver.scoped非常适合在鸿蒙多 HAP 模式下实现模块自治和强弱隔离。injector / flutter_simple_dependency_injection纯 Dart 库100% 兼容 OpenHarmony。支持通过工厂模式Factory和单例模式Singleton管理依赖非常适合用来屏蔽鸿蒙与 Android/iOS 的底层 API 差异实现一套代码多端运行。1、 拥抱自动化使用injectable与inject_generator在大型工程中手动维护注册和解析逻辑不仅繁琐还极易出错。引入代码生成器可以将开发者从繁琐的依赖树组装中解放出来。核心机制利用module、provide等注解标记依赖通过build_runner在编译期自动扫描并生成*.inject.dart文件构建好单例或工厂的实例化逻辑。鸿蒙适配优势由于生成的代码是标准的 Dart 实例化调用不涉及运行时反射因此不会带来额外的性能损耗且能在编译期提前发现依赖链条断裂或环形依赖的问题。1. 编写注解定义模块与依赖在业务代码中我们只需通过module和provide等注解告诉生成器“谁是谁的依赖”。// 1. 定义一个鸿蒙模块提供底层数据库服务 module class OhosMainModule { provide OhosDatabase provideDb() OhosDatabase(); } // 2. 定义业务服务声明它依赖 OhosDatabase injectable class OhosUserService { final OhosDatabase _db; // 构造函数中的参数生成器会自动寻找并注入 OhosUserService(this._db); void fetchUser() print(通过数据库获取用户); }2. 运行生成器触发自动化组装在终端中执行build_runner命令dart run build_runner build此时生成器会在后台自动分析OhosUserService与OhosMainModule之间的依赖树并在同级目录下生成一份名为*.inject.dart或*.g.dart的文件。在这个生成的文件中机器已经为你写好了类似如下的标准 Dart 实例化代码// 自动生成的代码开发者无需手写 OhosUserService createOhosUserService() { final db OhosDatabase(); // 自动实例化底层依赖 return OhosUserService(db); // 自动组装并返回完整对象 }3. 业务层使用零手动配置的解耦体验在鸿蒙应用的入口或页面中你只需要调用自动生成的容器即可获取组装好的服务void main() async { // 1. 初始化鸿蒙注入容器底层会自动调用生成器组装好的逻辑 final container await OhosAppComponent.create(OhosMainModule()); // 2. 直接获取服务无需关心 OhosUserService 是如何被 new 出来的 runApp(MyApp(userService: container.userService)); }2、 多环境隔离与动态适配Environments鸿蒙应用通常面临真机调试、模拟器运行、自动化测试ohos_test等多种环境。DI 框架应支持根据环境动态注入不同的实现环境标签利用Environment(ohos_real)或Environment(dev)为同一个接口定义多份实现。实战场景在开发环境下注入MockStorage内存缓存而在鸿蒙真机环境下自动注入NativeOhosStorage鸿蒙原生持久化存储。业务层无需修改任何代码即可实现跨环境的无缝切换。3、 异步服务的先行预解析Asynchronous Init鸿蒙系统中许多底层 API如Preferences.getInstance()、设备传感器初始化都是异步的。如果在 DI 容器就绪前就尝试获取这些服务会导致运行时异常。解决方案使用preResolve注解。它会强制 DI 容器在初始化阶段等待异步服务完成解析确保后续业务逻辑在调用getItT()时服务已完全就绪。module abstract class OhosPlatformModule { // 使用 preResolve 标记异步初始化服务 preResolve FutureSharedPreferences get prefs SharedPreferences.getInstance(); }4、 防御循环依赖与内存泄漏在复杂的模块化架构中A 依赖 B、B 又依赖 A 的“炸弹式堆栈溢出”是常见的崩溃诱因。延迟初始化Lazy Fetch在业务逻辑内部才调用df.get()或weaver.get()不要在构造函数的第一行就触发全量寻找将依赖解析的时间点向后推移。内存防御对于持有大体积数据如鸿蒙媒体缓存、大规模 JSON 字典的 Service应谨慎使用全局单例。建议在模块卸载或页面销毁时手动触发容器的unregister或配合weaver.scoped局部作用域确保依赖资源能被及时释放。5、 多 Isolate 间的“注入真空”处理由于鸿蒙的隔离空间Isolate不共享内存UI 线程注册的服务在后台计算线程中是拿不到的。独立注入空间在每一个新创建的 Isolate 起始处重新执行一次轻量级的注册逻辑。利用现代 DI 框架如df_di的极速启动特性这种重复注册的性能损耗可以忽略不计。参数序列化透传如果必须跨线程共享某些服务状态建议将对象关键参数序列化后通过消息队列如tw_queue进行跨线程传递。6、 分布式场景下的状态对齐在鸿蒙“万物互联”的分布式场景下多个设备副本可能同时运行着 DI 容器。如果某个服务需要在设备间保持状态一致如全局配置、分布式数据对象建议在 DI 的实现类内部调用鸿蒙原生的分布式数据对象Distributed Data Object接口实现“全局单例”在不同设备间的状态自动对齐。