0
# Module Management
1
2
Experimental API for dynamically loading and unloading Koin modules based on Compose lifecycle events, enabling dynamic dependency injection that responds to component composition and disposal.
3
4
## Capabilities
5
6
### Remember Koin Modules
7
8
Load and remember Koin modules with configurable lifecycle management, allowing modules to be automatically unloaded when components are forgotten or abandoned.
9
10
```kotlin { .api }
11
/**
12
* Load and remember Koin modules with lifecycle management
13
* @param unloadOnForgotten Unload modules on onForgotten event (optional)
14
* @param unloadOnAbandoned Unload modules on onAbandoned event (optional)
15
* @param unloadModules Unload modules on forgotten/abandoned (default: false)
16
* @param modules Lambda returning list of modules to load
17
*/
18
@KoinExperimentalAPI
19
@Composable
20
inline fun rememberKoinModules(
21
unloadOnForgotten: Boolean? = null,
22
unloadOnAbandoned: Boolean? = null,
23
unloadModules: Boolean = false,
24
crossinline modules: @DisallowComposableCalls () -> List<Module> = { emptyList() }
25
)
26
```
27
28
**Usage Examples:**
29
30
```kotlin
31
@Composable
32
fun DynamicFeature() {
33
// Basic module loading with automatic cleanup
34
rememberKoinModules(unloadModules = true) {
35
listOf(featureModule, networkModule)
36
}
37
38
// Content can now use dependencies from loaded modules
39
val featureService: FeatureService = koinInject()
40
41
FeatureContent(featureService)
42
}
43
44
@Composable
45
fun ConditionalModules(isPreview: Boolean) {
46
rememberKoinModules(
47
unloadOnForgotten = true,
48
unloadOnAbandoned = true
49
) {
50
if (isPreview) {
51
listOf(previewModule, mockModule)
52
} else {
53
listOf(productionModule, realNetworkModule)
54
}
55
}
56
57
Content()
58
}
59
```
60
61
## Module Lifecycle Strategies
62
63
### Automatic Cleanup
64
65
Modules can be automatically unloaded based on Compose RememberObserver events:
66
67
```kotlin
68
@Composable
69
fun AutoCleanupExample() {
70
// Modules unloaded when component leaves composition
71
rememberKoinModules(unloadModules = true) {
72
listOf(
73
temporaryModule,
74
featureSpecificModule
75
)
76
}
77
78
TemporaryFeatureContent()
79
}
80
```
81
82
### Fine-Grained Control
83
84
Control exactly when modules are unloaded:
85
86
```kotlin
87
@Composable
88
fun FineGrainedControl() {
89
rememberKoinModules(
90
unloadOnForgotten = true, // Unload when forgotten (recomposition skip)
91
unloadOnAbandoned = false // Keep loaded when abandoned
92
) {
93
listOf(persistentModule, cacheModule)
94
}
95
96
// Modules persist through abandonment but unload when forgotten
97
PersistentContent()
98
}
99
```
100
101
### Manual Module Management
102
103
Combine with conditional loading for manual control:
104
105
```kotlin
106
@Composable
107
fun ManualModuleControl() {
108
var modulesEnabled by remember { mutableStateOf(false) }
109
110
if (modulesEnabled) {
111
rememberKoinModules(unloadModules = true) {
112
listOf(conditionalModule)
113
}
114
}
115
116
Column {
117
Switch(
118
checked = modulesEnabled,
119
onCheckedChange = { modulesEnabled = it }
120
)
121
122
if (modulesEnabled) {
123
ModuleDependentContent()
124
}
125
}
126
}
127
```
128
129
## Dynamic Module Loading
130
131
### Conditional Module Sets
132
133
Load different modules based on runtime conditions:
134
135
```kotlin
136
@Composable
137
fun ConditionalModuleSets(userRole: UserRole, isDebug: Boolean) {
138
rememberKoinModules(unloadModules = true) {
139
buildList {
140
// Base modules always loaded
141
add(coreModule)
142
143
// Role-based modules
144
when (userRole) {
145
UserRole.ADMIN -> add(adminModule)
146
UserRole.USER -> add(userModule)
147
UserRole.GUEST -> add(guestModule)
148
}
149
150
// Debug modules
151
if (isDebug) {
152
add(debugModule)
153
add(loggingModule)
154
}
155
}
156
}
157
158
RoleBasedContent(userRole)
159
}
160
```
161
162
### Feature-Based Loading
163
164
Load modules based on enabled features:
165
166
```kotlin
167
@Composable
168
fun FeatureBasedLoading(enabledFeatures: Set<Feature>) {
169
rememberKoinModules(
170
unloadOnForgotten = true,
171
unloadOnAbandoned = false
172
) {
173
enabledFeatures.mapNotNull { feature ->
174
when (feature) {
175
Feature.ANALYTICS -> analyticsModule
176
Feature.PUSH_NOTIFICATIONS -> notificationModule
177
Feature.OFFLINE_MODE -> offlineModule
178
Feature.PREMIUM -> premiumModule
179
else -> null
180
}
181
}
182
}
183
184
FeatureContent(enabledFeatures)
185
}
186
```
187
188
## Integration with State
189
190
### State-Driven Module Loading
191
192
React to state changes to load/unload modules:
193
194
```kotlin
195
@Composable
196
fun StateDrivenModules() {
197
var connectionState by remember { mutableStateOf(ConnectionState.OFFLINE) }
198
199
// Modules change based on connection state
200
rememberKoinModules(unloadModules = true) {
201
when (connectionState) {
202
ConnectionState.ONLINE -> listOf(onlineModule, syncModule)
203
ConnectionState.OFFLINE -> listOf(offlineModule, cacheModule)
204
ConnectionState.UNKNOWN -> listOf(fallbackModule)
205
}
206
}
207
208
LaunchedEffect(Unit) {
209
networkStateFlow.collect { state ->
210
connectionState = state
211
}
212
}
213
214
ConnectionAwareContent(connectionState)
215
}
216
```
217
218
### Navigation-Based Loading
219
220
Load modules based on navigation state:
221
222
```kotlin
223
@Composable
224
fun NavigationBasedModules(navController: NavController) {
225
val currentDestination by navController.currentBackStackEntryAsState()
226
227
rememberKoinModules(unloadModules = true) {
228
when (currentDestination?.destination?.route) {
229
"profile" -> listOf(profileModule, userPreferencesModule)
230
"settings" -> listOf(settingsModule, configModule)
231
"shop" -> listOf(shopModule, paymentModule, cartModule)
232
else -> emptyList()
233
}
234
}
235
236
NavHost(navController = navController) {
237
// Navigation destinations
238
}
239
}
240
```
241
242
## Performance Considerations
243
244
### Module Loading Overhead
245
246
Be mindful of module loading performance:
247
248
```kotlin
249
@Composable
250
fun OptimizedModuleLoading(heavyFeatureEnabled: Boolean) {
251
// Only load heavy modules when actually needed
252
if (heavyFeatureEnabled) {
253
rememberKoinModules(unloadModules = true) {
254
listOf(
255
heavyComputationModule,
256
largeDataModule
257
)
258
}
259
260
HeavyFeatureContent()
261
} else {
262
LightweightContent()
263
}
264
}
265
```
266
267
### Module Reuse
268
269
Prevent unnecessary module reloading:
270
271
```kotlin
272
@Composable
273
fun EfficientModuleReuse(config: FeatureConfig) {
274
// Modules only reload when config.moduleSet changes
275
val moduleSet = remember(config.moduleSet) { config.moduleSet }
276
277
rememberKoinModules(unloadModules = true) {
278
moduleSet.map { moduleFactory ->
279
moduleFactory.create()
280
}
281
}
282
283
ConfigurableContent(config)
284
}
285
```
286
287
## Error Handling
288
289
### Module Loading Errors
290
291
Handle module loading failures gracefully:
292
293
```kotlin
294
@Composable
295
fun SafeModuleLoading() {
296
var moduleError by remember { mutableStateOf<String?>(null) }
297
298
try {
299
rememberKoinModules(unloadModules = true) {
300
listOf(
301
riskyModule,
302
dependentModule
303
)
304
}
305
306
NormalContent()
307
308
} catch (e: Exception) {
309
LaunchedEffect(e) {
310
moduleError = "Failed to load modules: ${e.message}"
311
}
312
}
313
314
moduleError?.let { error ->
315
ErrorMessage(error) {
316
moduleError = null
317
}
318
}
319
}
320
```
321
322
### Missing Module Dependencies
323
324
Handle cases where modules have missing dependencies:
325
326
```kotlin
327
@Composable
328
fun RobustModuleLoading() {
329
var fallbackMode by remember { mutableStateOf(false) }
330
331
if (!fallbackMode) {
332
try {
333
rememberKoinModules(unloadModules = true) {
334
listOf(primaryModule, dependencyModule)
335
}
336
337
PrimaryContent()
338
339
} catch (e: BeanCreationException) {
340
LaunchedEffect(e) {
341
fallbackMode = true
342
}
343
}
344
} else {
345
rememberKoinModules(unloadModules = true) {
346
listOf(fallbackModule)
347
}
348
349
FallbackContent()
350
}
351
}
352
```
353
354
## Testing Support
355
356
### Test Module Loading
357
358
Load test-specific modules in test environments:
359
360
```kotlin
361
@Composable
362
fun TestableComponent(isTest: Boolean = false) {
363
rememberKoinModules(unloadModules = true) {
364
if (isTest) {
365
listOf(testModule, mockModule)
366
} else {
367
listOf(productionModule, realModule)
368
}
369
}
370
371
ComponentContent()
372
}
373
374
// In tests
375
@Test
376
fun testComponent() {
377
composeTestRule.setContent {
378
TestableComponent(isTest = true)
379
}
380
381
// Test with mocked dependencies
382
}
383
```
384
385
### Preview Module Loading
386
387
Special modules for Compose previews:
388
389
```kotlin
390
@Preview
391
@Composable
392
fun ComponentPreview() {
393
rememberKoinModules {
394
listOf(previewModule)
395
}
396
397
ComponentContent()
398
}
399
400
val previewModule = module {
401
single<Repository> { MockRepository() }
402
single<ApiService> { PreviewApiService() }
403
}
404
```
405
406
## Best Practices
407
408
1. **Lifecycle Awareness**: Use appropriate unload strategies based on your use case
409
2. **Performance**: Load modules conditionally to avoid unnecessary overhead
410
3. **Error Handling**: Implement fallback strategies for module loading failures
411
4. **State Integration**: Tie module loading to relevant state changes
412
5. **Testing**: Provide test-specific module loading strategies
413
6. **Memory Management**: Use `unloadModules = true` for temporary features
414
7. **Module Design**: Design modules to be independently loadable and unloadable
415
8. **Experimental API**: Remember this is an experimental API and may change in future versions