Multi-Module Android Apps
This guide covers Android-specific aspects of multi-module architecture with Koin.
info
For core module concepts (includes(), organization, overrides), see Modules.
Android Application Setup
With Annotations
@KoinApplication(AppModule::class)
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin<MyApplication> {
androidLogger()
androidContext(this@MyApplication)
}
}
}
// Root module includes all features
@Module(includes = [LoginModule::class, HomeModule::class, ProfileModule::class])
class AppModule
With DSL
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@MyApplication)
modules(appModule) // Single root module
}
}
}
// Root module includes all features
val appModule = module {
includes(
loginModule,
homeModule,
profileModule
)
}
Feature Module Example
// :feature:login module
@KoinViewModel
class LoginViewModel(
private val loginUseCase: LoginUseCase,
private val userRepository: UserRepository
) : ViewModel()
@Factory
class LoginUseCase(private val authService: AuthService)
@Module(includes = [DataModule::class])
@ComponentScan("com.app.feature.login")
class LoginModule
// DSL equivalent
val loginModule = module {
includes(dataModule)
viewModel<LoginViewModel>()
factory<LoginUseCase>()
}
Dynamic Feature Loading
Load feature modules on-demand with Activity lifecycle:
class FeatureActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
loadKoinModules(featureModule)
super.onCreate(savedInstanceState)
}
override fun onDestroy() {
super.onDestroy()
unloadKoinModules(featureModule)
}
}
Koin vs Hilt Comparison
| Hilt | Koin |
|---|---|
@HiltAndroidApp | startKoin { androidContext() } |
@InstallIn(SingletonComponent) | Module loaded in startKoin {} |
@EntryPoint for cross-module | Automatic resolution |
| Component dependencies | includes() |
@ApplicationContext | androidContext() (automatic) |
info
Koin Advantage: No @EntryPoint interfaces needed. Dependencies resolve across modules automatically as long as all modules are loaded.
Android Testing
Test Module in Isolation
class LoginViewModelTest : KoinTest {
@get:Rule
val koinTestRule = KoinTestRule.create {
modules(
loginModule,
module {
single<UserRepository> { mockk() }
single<AuthService> { mockk() }
}
)
}
private val viewModel: LoginViewModel by inject()
@Test
fun `test login`() {
// Test with mocked dependencies
}
}
Verify All Modules
class ModuleCheckTest : KoinTest {
@Test
fun `verify all modules`() {
appModule.verify() // Verifies included modules too
}
}
See Also
- Modules - Core module concepts with
includes() - Android Module Loading - Dynamic module loading
- Lazy Modules - Background module loading
- Testing - Android testing guide