A lightweight extension library based on Hilt, providing Room multi-module aggregation and automated injection for dynamic proxy interfaces, solving 80% of boilerplate code with 20% of the code.
// build.gradle.kts
plugins {
id("com.google.devtools.ksp") version "2.1.10-1.0.31" // KSP required
}
dependencies {
implementation("com.google.dagger:hilt-android:2.56.2")
ksp("com.google.dagger:hilt-compiler:2.56.2")
implementation("top.cyclops:hilt-plus:version")
ksp("top.cyclops:hilt-plus-compiler:version")
}Mark interfaces with @HiltApi to automatically generate Hilt Provider modules. Developers only need to provide ApiCreator implementations (e.g., Retrofit instances).
@HiltApi
@ServerA // Optional: Use qualifier to differentiate services
interface GitHubApi {
@GET("/users/{name}")
suspend fun getUser(@Path("name") name: String): User
}@dagger.Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
@Provides
@ServerA // Match interface qualifier
fun provideApiCreator(retrofit: Retrofit): ApiCreator = retrofit.create(clazz)
}@HiltViewModel
class UserViewModel @Inject constructor(
@ServerA private val gitHubApi: GitHubApi // Auto-injected
) : ViewModel() {
fun loadUser(name: String) = viewModelScope.context {
val user = gitHubApi.getUser(name) // Direct call
}
}- 🚫 Traditional Issue: Manual aggregation of all Entities and Daos in
@Databasefor multi-module projects - ✅ Hilt Plus Solution: Automatic aggregation via
@HiltDao+ Module Nodes
@HiltDao(entities = [User::class]) // Declare associated Entity
@Dao
interface UserDao {
@Query("SELECT * FROM User WHERE id = :id")
fun getById(id: Long): Flow<User?>
}@HiltRoom(name = "app", version = 1)
@TypeConverters(LocalDateConverter::class)
interface AppDatabase : DatabaseTransaction // Must inherit transaction interface@AndroidEntryPoint
class UserRepository @Inject constructor(
private val userDao: UserDao, // Direct Dao injection
private val dbTransaction: DatabaseTransaction // Transaction management
) {
fun safeUpdate(user: User) = dbTransaction.runInTransaction {
userDao.update(user)
}
}// module_user module
@Qualifier
annotation class UserModuleNode // User module node
// module_music module
@Qualifier
annotation class MusicModuleNode // Music module node// In module_user
@HiltDao(
entities = [User::class],
node = UserModuleNode::class // Bind to user module node
)
@Dao
interface UserDao { /* ... */ }
// In module_music
@HiltDao(
entities = [Song::class],
node = MusicModuleNode::class // Bind to music module node
)
@Dao
interface SongDao { /* ... */ }@HiltRoom(
name = "main",
version = 1,
nodes = [UserModuleNode::class, MusicModuleNode::class] // Aggregate modules
)
interface MainDatabase : DatabaseTransaction@AndroidEntryPoint
class MusicService @Inject constructor(
@MusicModuleNode // Qualifier specifies module node
private val transaction: DatabaseTransaction
) {
fun importSongs(songs: List<Song>) = transaction.withTransction {
// Use music module's transaction context
}
}| Feature | Traditional Approach | Hilt Plus |
|---|---|---|
| Retrofit Injection | Manual @Provides modules |
Auto-generated via @HiltApi |
| Room Multi-module | Manual Dao registration in main module | Auto-aggregation via @HiltDao |
| Transaction Mgmt | Via @Database instance |
Direct DatabaseTransaction injection |
-
KSP Version Alignment
Ensurekspplugin version matches Kotlin version. -
Module Visibility
In multi-module projects,@HiltDaointerfaces must be visible to main module (avoidinternal). -
ProGuard Rules
Add rules to retain generated_Databaseand_Implclasses if using obfuscation.
Visit GitHub Repository for:
- 🛠️ Single-module Demo
- 🧩 Multi-module Aggregation Example