What is Modularization in Android?
Modularization in Android means breaking an app into smaller, independent modules instead of keeping all code in a single project. Each module has its own responsibility and can be developed, tested, and maintained separately.
Basic Prerequisites to Understand Modularization
Before learning modularization, you should be familiar with:
- Android Project Structure – Understanding how an app is built using app/src/main.
- Gradle & Dependencies – Knowing how dependencies are managed in build.gradle.kts.
- Packages & Code Organization – Basics of organizing files using packages.
- Types of Android Modules –
- App Module (Main application)
- Library Module (Reusability across projects)
- Feature Module (For dynamic delivery in large apps)
Why Modularization?
✅ Faster Build Times – Only modified modules get compiled.
✅ Better Code Maintainability – Easier to manage and scale.
✅ Reusability – Common features like networking or authentication can be shared across projects.
✅ Parallel Development – Teams can work on different modules independently.
Today we are going to set up step by step how to use Koin in modularization with Room, Retrofit, and viewModel (along with UseCase and Repository).
Before we get started. Create some new modules and structured them like this:
Step 1
You have to determine what koin gradle version you want to use. For this tutorial, I use 2.2.2
version. Define the gradle into di
module
implementation "io.insert-koin:koin-android:2.2.2"
implementation "io.insert-koin:koin-androidx-viewmodel:2.2.2"
implementation "io.insert-koin:koin-androidx-scope:2.2.2"
Step 2
After you have decided koin version you'd use, add other dependency your project would need for injecting to koin in di
module as well. In this tutorial I want to inject Retrofit
for network, AndroidX Room
for local DB, Shared Preferences
, ViewModel
for managing data, and Coroutine
for threading
implementation "io.insert-koin:koin-android:2.2.2"
implementation "io.insert-koin:koin-androidx-viewmodel:2.2.2"
implementation "io.insert-koin:koin-androidx-scope:2.2.2"
implementation "javax.inject:javax.inject:1"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
implementation "androidx.room:room-runtime:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.room:room-ktx:2.4.2"
Step 3
Define Network Module, Api Module, Shared Preferences Module, Room Module, and ViewModel Module (we also use UseCase and Repository module for clean architecture principle)
class ApiModule {
companion object {
val apiModule = module(override = true) {
single { provideDisneyApi(get()) }
}
fun provideDisneyApi(retrofit: Retrofit): Api {
return retrofit.create(Api::class.java)
}
}
}
class NetworkModule {
companion object {
private fun httpInterceptor() = HttpLoggingInterceptor().apply {
return HttpLoggingInterceptor { _ ->
}.apply {
level = HttpLoggingInterceptor.Level.BODY
}
}
fun provideNetworkHandler(context: Context) = NetworkHandler(context)
fun provideOkHttpClient() : OkHttpClient {
return Builder.initInterceptor(httpInterceptor())
}
fun provideRetrofitService(okHttpClient: OkHttpClient): Retrofit {
return Builder.initRetrofit(okHttpClient)
}
}
}
class SharedPrefModule {
companion object {
fun providePreference(context: Context) : SharedPreferences {
return context.getSharedPreferences("SharedPreferenceName", Context.MODE_PRIVATE)
}
}
}
class RoomModule {
companion object {
fun provideRoom(context: Context) : DBConfig {
return Room.databaseBuilder(context, DBConfig::class.java, "db_sample")
.fallbackToDestructiveMigration()
.build()
}
}
}
class RepositoryModule {
companion object {
val repositoryModule = module(override = true) {
single<CharacterRepository> { return@single CharacterRepositoryImpl(get(), get()) }
}
}
}
class UseCaseModule {
companion object {
val useCaseModule = module(override = true) {
single<CharacterUseCase> { return@single CharacterUseCaseImpl(get()) }
}
}
}
class ViewModelModule {
companion object {
val viewModelModule = module(override = true) {
viewModel { GamesListVM(get(), get()) }
viewModel { GamesDetailVM(get(), get()) }
viewModel { GamesFavoriteVM(get(), get(), get()) }
}
}
}
After you add them all, locate and categorize module that you prioritize as a 'core' module and wrap them into AppModule
class. In this tutorial I'll wrap retrofit
, room
, and shared preferences
module like this:
val MainAppModule = module(override = true) {
single { NetworkModule.provideOkHttpClient() }
single { NetworkModule.provideRetrofitService(get()) }
single { NetworkModule.provideNetworkHandler(androidContext()) }
single { RoomModule.provideRoom(androidContext()) }
single { SharedPrefModule.providePreference(androidContext()) }
}
Step 4
Call them with StartKoin
in Application Class
in App Module (default module). I will call ViewModel, Repository, Usecase, and Api module as same level with MainApp Module
class DisneyApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger(Level.ERROR)
androidContext(this@DisneyApp)
modules(MainAppModule, ApiModule.apiModule, RepositoryModule.repositoryModule,
UseCaseModule.useCaseModule, ViewModelModule.viewModelModule
)
}
}
}
And Voila! Congratulation you've built Koin in modular version. it's easy isn't it?
Happy Coding :)
For sample project, you can refer to this Github
Top comments (0)