Contexto
En clean architecture con Android, terminas con dos tipos distintos de dependencias para inyectar:
- Interfaces -> implementaciones (BlockedNumberRepository -> BlockedNumberRepositoryImpl)
- Objetos construidos (Room database, DAOs)
Tentacion de novato: usar @Provides para todo. Funciona, pero genera codigo subobtimo y no transmite intent.
Lo que aprendi
@Binds es para mapear interfaz a implementacion cuando el constructor de la implementacion ya esta inyectado:
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindBlockedNumberRepository(
impl: BlockedNumberRepositoryImpl
): BlockedNumberRepository
@Binds
@Singleton
abstract fun bindBlockedCallRepository(
impl: BlockedCallRepositoryImpl
): BlockedCallRepository
}
Notar que el modulo es abstract class, no object. Hilt no necesita instanciarlo porque @Binds se resuelve en compilacion.
@Provides es para cuando construyes el objeto manualmente:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, AppDatabase.DATABASE_NAME)
.addMigrations(
Migrations.MIGRATION_1_2, Migrations.MIGRATION_2_3,
Migrations.MIGRATION_3_4, Migrations.MIGRATION_4_5,
Migrations.MIGRATION_5_6, Migrations.MIGRATION_6_7
)
.build()
}
@Provides
fun provideBlockedNumberDao(database: AppDatabase) = database.blockedNumberDao()
}
Aqui el modulo es object, porque Hilt si necesita ejecutar provideAppDatabase() para obtener la instancia.
Por que importa
@Binds genera codigo mas eficiente (Hilt lo resuelve sin reflexion ni instancia del modulo). Ademas comunica intent al lector: "esto es un mapeo, no una construccion". Cuando otro dev abre RepositoryModule.kt ve "abstract class + @Binds" y sabe inmediatamente que no hay logica custom.
Regla rapida:
- Tu clase tiene
@Inject constructor? ->@Bindscon abstract module - Construyes manualmente con builder, factory, o config? ->
@Providescon object module
Mezclarlos en el mismo proyecto esta perfecto. Mezclarlos en el mismo modulo no compila.