Koin est un framework DI pour Kotlin qui fonctionne sans génération de code, réflexion ni annotations. La bibliothèque utilise DSL pour décrire les modules et injecte les dépendances via un conteneur léger avec prise en charge d'Android, Ktor et Multiplatform. Selon la documentation officielle de Koin, le framework fournit des modules, des scopes et un support intégré pour Jetpack Compose avec un minimum de boilerplate.
Points clés
Koin est un framework DI pour Kotlin écrit en langage pur sans utiliser de réflexion, d'annotations ou de génération de code. Contrairement à Dagger Hilt, qui nécessite un processeur d'annotations et une génération de code à la compilation, Koin fonctionne exclusivement à l'exécution en utilisant un DSL léger pour décrire les modules.
L'idée principale de Koin est de fournir une API simple pour enregistrer et résoudre les dépendances sans avoir à apprendre des concepts complexes de graphes de dépendances et d'arbres de composants. Le développeur décrit quelles classes sont disponibles pour le conteneur, et Koin les injecte automatiquement via le constructeur ou des délégués paresseux by inject. Le framework est entièrement compatible avec Kotlin Multiplatform, permettant une approche DI unifiée sur Android, iOS et côté serveur.
Selon l'enquête de la Kotlin Developers Community (2025), Koin est utilisé dans 31% des projets Android commerciaux, juste derrière Hilt (47%). La raison principale de ce choix est la facilité de configuration et l'absence de besoin de génération de code, ce qui accélère la compilation du projet.
Choisissez Koin pour les projets moyens et grands où un démarrage rapide du développement est important, ou pour les solutions Kotlin Multiplatform où Hilt n'est pas disponible pour des raisons architecturales.
Koin n'utilise ni réflexion ni génération de code — tous les enregistrements sont construits sur des fonctions inline avec des types reified qui substituent le type concret dans le corps de la fonction à la compilation. Cela fait de Koin l'un des frameworks DI les plus légers en termes de taille finale d'APK : ajouter Koin augmente la taille de l'application de seulement 100 à 150 Ko, tandis que Dagger Hilt ajoute environ 500 Ko à cause du code généré.
Le conteneur Koin est initialisé via la fonction startKoin, qui accepte une lambda avec configuration. À l'intérieur de cette lambda, les modules avec enregistrements — le bloc de construction principal de la logique DI — sont décrits.
La fonction startKoin crée un conteneur global accessible depuis n'importe où dans l'application via GlobalContext, cependant dans les projets multi-modules, il est recommandé d'utiliser KoinApplication pour créer des conteneurs isolés. Dans Android, AndroidContext est utilisé pour l'initialisation, qui est automatiquement lié au cycle de vie d'Application. Les modules sont enregistrés via le paramètre modules, qui accepte une liste d'instances de Module.
val networkModule = module {
single {
OkHttpClient()
}
single {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
startKoin {
modules(networkModule)
}
Chaque module contient des définitions via single (singleton) ou factory (nouvelle instance). Les définitions peuvent référencer d'autres dépendances enregistrées via get(), formant un graphe d'injection sans spécification explicite de type et sans code boilerplate.
Koin utilise activement des fonctions inline avec des paramètres reified pour l'inférence de type à partir du contexte. Cela permet d'écrire des enregistrements sans spécifier explicitement la classe : single { MyService() } détermine automatiquement le type par la valeur de retour de la lambda.
Contrairement à Dagger, Koin ne vérifie pas le graphe de dépendances à la compilation — toutes les erreurs sont détectées à l'exécution lors du premier accès à une dépendance non résolue. C'est un compromis qui simplifie considérablement le code et accélère les compilations, mais nécessite une couverture de test de la configuration DI. De nombreuses équipes choisissent Koin précisément pour la rapidité de développement et la simplicité, malgré l'absence de vérifications à la compilation.
Dans la version Koin 3.5, une vérification expérimentale du graphe à la compilation est apparue via le plugin Koin Annotations. Le développeur ajoute les annotations @Module et @KoinComponent, et le plugin génère du code de validation qui s'exécute lors de la compilation. Cependant, le principal avantage de Koin — l'absence de génération de code — est perdu dans ce mode, donc la plupart des équipes continuent d'utiliser l'approche DSL classique avec des vérifications à l'exécution via des tests.
Koin fournit plusieurs façons d'injecter des dépendances : by inject(), get() et passage direct par constructeur. Le choix dépend du contexte d'utilisation.
Le délégué by inject est la méthode d'injection la plus courante dans les ViewModels et fragments Android. La dépendance est initialisée paresseusement — seulement au premier accès à la propriété. C'est efficace pour les services gourmands en ressources qui peuvent ne pas être nécessaires immédiatement.
class MainViewModel : ViewModel() {
private val repository: UserRepository by inject()
fun loadUsers() {
repository.fetchAll()
}
}
La fonction get retourne une instance de dépendance immédiatement. Elle est utilisée à l'intérieur de lambdas factory lors de l'enregistrement ou lorsque la dépendance est nécessaire dans un contexte synchrone sans initialisation paresseuse. Contrairement à by inject(), get() ne prend pas en charge le chargement paresseux et nécessite que le conteneur soit déjà initialisé au moment de l'appel.
Scope dans Koin est un mécanisme pour lier la durée de vie des dépendances à un composant spécifique, comme une Activity, un Fragment ou une session personnalisée. C'est une fonctionnalité clé pour la gestion de la mémoire dans les applications Android.
La fonction scope à l'intérieur d'un module crée un scope qui vit tant que le composant lié vit. Toutes les dépendances enregistrées dans le scope sont détruites lorsqu'il est fermé, ce qui empêche les fuites de mémoire.
val userScope = module {
scope<UserSession> {
scoped {
UserRepository(get())
}
scoped {
SessionManager(get())
}
}
}
La fonction scoped enregistre une dépendance qui n'existera qu'à l'intérieur du scope. Lorsque le scope est fermé, tous les objets scoped deviennent disponibles pour le garbage collector.
single enregistre une instance unique pour toute l'application avec initialisation paresseuse. Utilisé pour les services sans état : clients réseau, caches, journaux.
factory crée une nouvelle instance à chaque appel get(). Appliqué aux ViewModel, référentiels et objets avec état où une nouvelle instance est importante à chaque accès.
L'intégration de Koin dans un projet Android est minimale : il suffit d'ajouter une dépendance dans build.gradle et d'appeler startKoin dans Application.onCreate. Koin fournit des modules pour l'intégration avec Jetpack Compose, Navigation et WorkManager, ce qui en fait une alternative complète à Hilt.
La bibliothèque spéciale koin-android-compose permet d'injecter des dépendances directement dans les fonctions Composable via koinViewModel() et koinInject(). Cela élimine la nécessité de passer le conteneur par les paramètres de chaque écran et rend le code ViewModel plus propre grâce à la liaison automatique au cycle de vie.
Selon Google I/O 2024, Jetpack Compose est devenu le framework principal pour les nouveaux projets Android. Koin fournit un support natif pour Compose sans configuration supplémentaire, liant automatiquement les scopes au cycle de vie du ViewModel via koinViewModel() en tenant compte du contexte de coroutine.
Pour les tests, Koin fournit les fonctions koinTest et koinTestRule, créant un conteneur de test isolé avec des modules de test et le fermant automatiquement après la fin du test. Cela garantit l'isolation des tests et empêche les fuites d'état entre les cas de test.
L'intégration de Koin avec Jetpack Navigation est implémentée via le module koin-androidx-navigation. Le ViewModel de chaque écran reçoit automatiquement les dépendances via by viewModel() avec passage de SavedStateHandle pour préserver l'état lors de la rotation de l'écran et le récupérer après la suspension de l'application.
Pour les tests unitaires de ViewModel avec Koin, koinTestRule de la bibliothèque koin-test-junit5 ou koin-test-junit4 est utilisé. La règle crée un conteneur isolé avec des modules de test avant chaque test et le ferme automatiquement après la fin, empêchant les fuites d'état entre les cas de test. Les dépendances réelles sont remplacées par des mocks via MockK : un module avec des enregistrements single
L'une des principales fonctionnalités de Koin 3.x est la prise en charge de Ktor pour créer des applications serveur en Kotlin et Compose Multiplatform pour les applications de bureau. Cela fait de Koin le seul framework DI couvrant les trois plateformes Kotlin sans changer le paradigme d'injection. Le module koin-ktor permet d'enregistrer des dépendances via install(Koin) dans le bloc Application et d'injecter des services dans les routes via by inject() comme dans Android. Cela fait de Koin une solution DI universelle pour les projets Kotlin de toute architecture — du client mobile au backend serveur.
L'intégration de Koin avec Jetpack Navigation via le module koin-androidx-navigation élimine la nécessité de créer manuellement un ViewModelProvider.Factory pour chaque écran. Pour les projets multi-modules, Koin prend en charge le chargement paresseux des modules via loadKoinModules, permettant à chaque module de fonctionnalité de connecter sa configuration DI indépendamment.
Questions fréquentes
Koin fonctionne à l'exécution sans génération de code ni annotations, ce qui accélère les compilations mais ne vérifie pas le graphe de dépendances à la compilation. Hilt génère du code à la compilation et détecte les erreurs DI plus tôt, mais nécessite une configuration complexe et ralentit la compilation.
Oui, Koin prend entièrement en charge Kotlin Multiplatform. La bibliothèque koin-core fonctionne sur toutes les plateformes Kotlin, tandis que koin-android et koin-compose ajoutent des capacités spécifiques à la plateforme pour Android et iOS respectivement.
Les dépendances circulaires entraînent une StackOverflowError à l'exécution. Koin ne les détecte pas automatiquement. La solution est le refactoring de l'architecture : extraction d'une interface commune, utilisation du pattern Listener/Observer ou rupture du cycle via une fabrique avec initialisation différée.
Dans Android, les scopes peuvent être liés au cycle de vie de l'Activity ou du Fragment via AndroidScope. Lorsque le composant est détruit, Koin ferme automatiquement le scope correspondant. Dans les scopes personnalisés (session utilisateur), la fermeture est effectuée manuellement en appelant scope.close.
Utilisez la fonction koinTest du module koin-test. Elle crée un conteneur isolé avec des modules de test qui se ferme automatiquement après le test. Les dépendances réelles sont remplacées par des mocks via un module avec Mockito ou MockK.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi