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

遵循CLEAN架构,Android领域层(Dagger)与应用/数据层(Hilt)集成问题

领域层用Dagger、应用/数据层用Hilt的架构实现问题

我是Android开发新手,正在研究应用架构的关注点分离,尤其是领域层、数据层和应用层。我希望领域层独立于Android框架,作为纯Kotlin库存在,所以领域层只用Dagger,不引入Hilt;应用层和数据层则使用Hilt。

当前架构设置:

  • 领域层:定义Repository接口、UseCase接口及其实现,用Dagger的Module提供依赖
  • 数据层:实现Repository接口,用Hilt管理依赖
  • 应用层:管理Activity和Hilt应用配置

尝试将UseCase注入Activity时遇到编译错误,错误信息如下:

app/build/generated/hilt/component_sources/debug/application/MyApp_HiltComponents.java:137: error: [Dagger/MissingBinding] application.domain.usecases.MyUseCase cannot be provided without an @Provides-annotated method.
  public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
                         ^
  
  Missing binding usage:
      application.domain.usecases.MyUseCase is injected at
          application.ui.MainActivity.myUseCase
      application.ui.MainActivity is injected at
          application.ui.MainActivity_GeneratedInjector.injectMainActivity(application.ui.MainActivity) [application.MyApp_HiltComponents.SingletonC → application.MyApp_HiltComponents.ActivityRetainedC → application.MyApp_HiltComponents.ActivityC]

简化代码

领域层(仅Dagger,无Hilt)

interface MyRepository {
    suspend fun findAllUser(): Flow<List<User>>
}

interface MyUseCase {
    suspend fun findAllUserRandom(): Flow<List<User>>
}

class MyUseCaseImpl @Inject constructor(
    private val myRepository: MyRepository,
): MyUseCase {
    override suspend fun findAllUserRandom(): Flow<List<User>> {
        return myRepository.findAll().map { list ->
            list.filter { 
                Random.nextInt() % 2 == 0
            }
        }
    }
}

@Module
class DomainModule {
    @Provides
    @Singleton
    fun provideMyUseCase(myRepository: MyRepository): MyUseCase {
        return MyUseCaseImpl(myRepository)
    }
}

数据层(用Hilt)

class MyRepositoryImpl @Inject constructor(
    private val myRoomDao: MyRoomDao,
): MyRepository {
    override suspend fun findAllUser(): Flow<List<User>> {
        return myRoomDao.findAll()
    }
}

@Module
@InstallIn(SingletonComponent::class)
interface DataModule {

    companion object {
        @Singleton
        @Provides
        fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
            return Room.databaseBuilder(
                context,
                AppDatabase::class.java,
                "my_database"
            ).build()
        }

        @Singleton
        @Provides
        fun provideMyRoomDao(appDatabase: AppDatabase): MyRoomDao {
            return appDatabase.myRoomDao()
        }
    }

    @Binds
    @Singleton
    fun bindMyRepository(myRepositoryImpl: MyRepositoryImpl): MyRepository
}

应用层

@HiltAndroidApp
class MyApp : Application()

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var myUseCase: MyUseCase // 编译错误

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Text("Hello World !")
                }
            }
        }
    }
}

我尝试通过在应用层模块中引入DomainModule来解决问题,代码如下,但不确定这是否是最优方案:

@InstallIn(SingletonComponent::class)
@Module(
    includes = [
        DomainModule::class
    ]
)
interface DataModule

我的疑问:是否可以在领域层仅使用Dagger,其余层(应用、数据)使用Hilt?如果可行,该如何正确实现此架构?


解决方案

可行性确认

完全可以实现领域层用纯Dagger、应用/数据层用Hilt的架构——因为Hilt本质是Dagger的Android扩展,二者可以无缝兼容。领域层保持纯Kotlin库的独立性,不依赖Android框架和Hilt,只需要用标准Dagger注解定义模块和依赖即可。

正确实现方式

你尝试的**在Hilt模块中引入Dagger的DomainModule**是完全合理且最优的方案之一,具体原因和优化点如下:

1. 核心原理

Hilt的组件(比如SingletonComponent)会自动包含所有通过@InstallIn指定的模块,以及这些模块includes的其他Dagger模块。把DomainModule通过includes加入到Hilt的模块中,就能让Hilt的组件识别到领域层提供的MyUseCase绑定,解决MissingBinding错误。

2. 优化实现

你当前的代码可以直接运行,但可以做一点小调整让结构更清晰:

  • 领域层的DomainModule保持原样(纯Dagger,无Hilt依赖)
  • 在应用层单独创建一个Hilt模块来引入领域层模块,而不是和数据层模块合并,这样职责更单一:
// 应用层的Hilt模块,专门负责引入领域层的Dagger模块
@InstallIn(SingletonComponent::class)
@Module(includes = [DomainModule::class])
class DomainHiltBridgeModule

这样数据层的DataModule只负责数据相关的绑定,DomainHiltBridgeModule专门做领域层和Hilt的桥接,架构分层更清晰。

3. 其他注意事项

  • 领域层的@Singleton注解要和Hilt的SingletonComponent兼容:因为Hilt的SingletonComponent对应应用生命周期的单例,和Dagger的@Singleton语义一致,所以领域层用@Singleton没问题。
  • 确保领域层的依赖都能被Hilt提供:比如MyRepository由数据层的Hilt模块绑定,所以领域层的MyUseCase依赖能被正确注入。

为什么你的尝试有效

当你在DataModule中includes = [DomainModule::class]时,Hilt会把DomainModule中的provideMyUseCase方法加入到自己的组件绑定图中,这样在注入MainActivity的myUseCase时,Hilt就能找到对应的提供方法,解决编译错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 04:52:14