Koin on modularization: How to set it?

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 viewModel. 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 { return@single CharacterRepositoryImpl(get(), get()) } } } } class UseCaseModule { companion object { val useCaseModule = module(override = true) { single { 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 int

Mar 15, 2025 - 18:50
 0
Koin on modularization: How to set it?

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 viewModel.

Before we get started. Create some new modules and structured them like this:
package_level

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 { return@single CharacterRepositoryImpl(get(), get()) }
        }
    }
}
class UseCaseModule {
    companion object {
        val useCaseModule = module(override = true) {
            single { 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