You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多模块Android架构中Hilt模块的优雅结构化方案问询

Hilt多模块依赖图接入问题解决方案

问题背景

当前使用Hilt 2.44.2版本,模块架构如下:

  • :app模块:表示层,包含Activity、导航、Compose/视图、ViewModel、生命周期处理等,依赖:domain与:core模块;
  • :domain模块:领域层,包含领域模型、用例及用例中使用的Repository接口,负责业务逻辑处理,依赖:core模块;
  • :data模块:数据层,处理网络、数据库及相关传输模型,同时实现:domain中定义的Repository接口,依赖:domain与:core模块;
  • :core模块:核心层,提供各模块共享的工具与通用逻辑。

核心问题:由于:data依赖:domain而非反向依赖,:data的Hilt Modules无法接入整体依赖图。常规方案是让:app依赖:data,但限制条件为不允许:app访问:data的类,也不想建立该依赖关系。

解决方案:Gradle配置隔离+Hilt组件扫描

步骤1:在:data模块中配置自定义Gradle依赖配置

在:data模块的build.gradle.kts中添加以下配置,将Hilt Modules相关编译产物单独暴露:

configurations {
    create("hiltModules") {
        isCanBeConsumed = true
        isCanBeResolved = false
        attributes {
            attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME))
        }
    }
}

artifacts {
    add("hiltModules", sourceSets.main.get().output)
}

此配置会把:data的编译产物(包含Hilt Modules)单独放到hiltModules配置中,而非默认的implementation配置,避免业务类被意外引用。

步骤2:在:app模块中仅依赖:data的Hilt Modules配置

在:app模块的build.gradle.kts中添加依赖,同时限制依赖类型:

dependencies {
    // 编译时仅获取Hilt模块元数据,不允许引用业务类
    compileOnly(project(path = ":data", configuration = "hiltModules"))
    // 运行时加载Hilt模块实现,完成依赖注入
    runtimeOnly(project(path = ":data", configuration = "hiltModules"))
}

compileOnly保证:app编译阶段无法访问:data的业务类(如传输模型、具体网络实现),runtimeOnly确保Hilt在运行时能找到并加载:data中的Repository实现。

步骤3:确保:data的Hilt Modules使用正确的@InstallIn注解

在:data模块的Hilt Modules上使用全局组件注解,比如:

@Module
@InstallIn(SingletonComponent::class)
class DataModule {
    @Provides
    fun provideXXXRepository(): XXXRepository {
        return XXXRepositoryImpl()
    }
}

@InstallIn(SingletonComponent::class)会让Hilt在应用启动时自动扫描并加载该模块,无需:app显式引用。

原理说明

  • 通过Gradle自定义配置,实现了:data模块中Hilt Modules与业务代码的隔离暴露,避免:app依赖整个:data模块;
  • compileOnly+runtimeOnly的组合,既保证了编译阶段的类隔离,又满足了运行时依赖注入的需求;
  • Hilt的组件扫描机制会自动识别@InstallIn注解的模块,完成依赖图的整合。

替代方案:@EntryPoint(适用于单一接口注入场景)

如果仅需注入少数几个Repository接口,可在:domain模块中定义@EntryPoint:

// :domain模块中
@EntryPoint
@InstallIn(SingletonComponent::class)
interface XXXRepositoryEntryPoint {
    fun getXXXRepository(): XXXRepository
}

然后在:app中通过EntryPointAccessors获取实例:

val repository = EntryPointAccessors.fromApplication(
    context,
    XXXRepositoryEntryPoint::class.java
).getXXXRepository()

此方案无需修改Gradle配置,但需逐个定义EntryPoint,适合场景单一的情况。

内容的提问来源于stack exchange,提问作者Yokich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:05:17