android-sdd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hilt
|
|
3
|
+
description: >
|
|
4
|
+
Hilt dependency injection setup and patterns for Android.
|
|
5
|
+
Load this skill when setting up Hilt, defining modules, injecting into
|
|
6
|
+
Android components, scoping dependencies, or providing third-party libraries.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Hilt
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Hilt is Android's recommended DI framework built on top of Dagger. It reduces Dagger boilerplate by providing standard components and scopes for Android classes. Hilt generates DI code at compile time — no reflection, no runtime overhead.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- **One scope per lifetime** — match dependency scope to its actual lifetime
|
|
19
|
+
- Never inject into constructors of Android framework classes — use `@Inject` on fields or use Hilt entry points
|
|
20
|
+
- Prefer **constructor injection** over field injection everywhere possible
|
|
21
|
+
- Modules provide dependencies the constructor can't — third-party libs, interfaces, context-dependent types
|
|
22
|
+
- **`@Singleton`** is application-scoped — use sparingly, only for truly shared state
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
[versions]
|
|
30
|
+
hilt = "2.51.1"
|
|
31
|
+
|
|
32
|
+
[libraries]
|
|
33
|
+
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
|
34
|
+
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
|
|
35
|
+
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version = "1.2.0" }
|
|
36
|
+
|
|
37
|
+
[plugins]
|
|
38
|
+
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```kotlin
|
|
42
|
+
// build.gradle.kts (root)
|
|
43
|
+
plugins {
|
|
44
|
+
alias(libs.plugins.hilt) apply false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// build.gradle.kts (app)
|
|
48
|
+
plugins {
|
|
49
|
+
alias(libs.plugins.hilt)
|
|
50
|
+
alias(libs.plugins.ksp)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dependencies {
|
|
54
|
+
implementation(libs.hilt.android)
|
|
55
|
+
ksp(libs.hilt.compiler)
|
|
56
|
+
implementation(libs.hilt.navigation.compose)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```kotlin
|
|
61
|
+
// ✅ Application class — required
|
|
62
|
+
@HiltAndroidApp
|
|
63
|
+
class MyApplication : Application()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Constructor Injection
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
// ✅ Preferred — inject via constructor
|
|
72
|
+
class UserRepository @Inject constructor(
|
|
73
|
+
private val userDao: UserDao,
|
|
74
|
+
private val userApi: UserApi,
|
|
75
|
+
private val mapper: UserMapper
|
|
76
|
+
) {
|
|
77
|
+
// Hilt provides all dependencies automatically
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class UserMapper @Inject constructor() {
|
|
81
|
+
fun toDomain(entity: UserEntity): User = TODO()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ✅ ViewModel — use @HiltViewModel
|
|
85
|
+
@HiltViewModel
|
|
86
|
+
class UserListViewModel @Inject constructor(
|
|
87
|
+
private val repository: UserRepository,
|
|
88
|
+
private val savedStateHandle: SavedStateHandle
|
|
89
|
+
) : ViewModel()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Hilt Modules
|
|
95
|
+
|
|
96
|
+
```kotlin
|
|
97
|
+
// ✅ @Module + @InstallIn — provides dependencies Hilt can't construct automatically
|
|
98
|
+
|
|
99
|
+
// Singleton module — app-scoped dependencies
|
|
100
|
+
@Module
|
|
101
|
+
@InstallIn(SingletonComponent::class)
|
|
102
|
+
object NetworkModule {
|
|
103
|
+
|
|
104
|
+
@Provides
|
|
105
|
+
@Singleton
|
|
106
|
+
fun provideOkHttpClient(): OkHttpClient =
|
|
107
|
+
OkHttpClient.Builder()
|
|
108
|
+
.connectTimeout(30, TimeUnit.SECONDS)
|
|
109
|
+
.build()
|
|
110
|
+
|
|
111
|
+
@Provides
|
|
112
|
+
@Singleton
|
|
113
|
+
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
|
|
114
|
+
Retrofit.Builder()
|
|
115
|
+
.baseUrl("https://api.example.com/")
|
|
116
|
+
.client(okHttpClient)
|
|
117
|
+
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
|
118
|
+
.build()
|
|
119
|
+
|
|
120
|
+
@Provides
|
|
121
|
+
@Singleton
|
|
122
|
+
fun provideUserApi(retrofit: Retrofit): UserApi =
|
|
123
|
+
retrofit.create(UserApi::class.java)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Module
|
|
127
|
+
@InstallIn(SingletonComponent::class)
|
|
128
|
+
object DatabaseModule {
|
|
129
|
+
|
|
130
|
+
@Provides
|
|
131
|
+
@Singleton
|
|
132
|
+
fun provideDatabase(@ApplicationContext context: Context): AppDatabase =
|
|
133
|
+
Room.databaseBuilder(context, AppDatabase::class.java, "app_db")
|
|
134
|
+
.addMigrations(MIGRATION_1_2)
|
|
135
|
+
.build()
|
|
136
|
+
|
|
137
|
+
@Provides
|
|
138
|
+
fun provideUserDao(database: AppDatabase): UserDao = database.userDao()
|
|
139
|
+
|
|
140
|
+
@Provides
|
|
141
|
+
fun provideOrderDao(database: AppDatabase): OrderDao = database.orderDao()
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Binding Interfaces
|
|
148
|
+
|
|
149
|
+
```kotlin
|
|
150
|
+
// ✅ Bind interface to implementation
|
|
151
|
+
@Module
|
|
152
|
+
@InstallIn(SingletonComponent::class)
|
|
153
|
+
abstract class RepositoryModule {
|
|
154
|
+
|
|
155
|
+
@Binds
|
|
156
|
+
@Singleton
|
|
157
|
+
abstract fun bindUserRepository(
|
|
158
|
+
impl: UserRepositoryImpl
|
|
159
|
+
): UserRepository
|
|
160
|
+
|
|
161
|
+
@Binds
|
|
162
|
+
abstract fun bindAnalytics(
|
|
163
|
+
impl: FirebaseAnalyticsImpl
|
|
164
|
+
): Analytics
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ✅ @Binds is more efficient than @Provides for interface binding
|
|
168
|
+
// Use @Provides only when you need to call a constructor or factory
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Scopes
|
|
174
|
+
|
|
175
|
+
```kotlin
|
|
176
|
+
// ✅ Scope reference
|
|
177
|
+
@Singleton // lives as long as Application
|
|
178
|
+
@ActivityRetainedScoped // lives as long as ViewModel (survives rotation)
|
|
179
|
+
@ActivityScoped // lives as long as Activity
|
|
180
|
+
@FragmentScoped // lives as long as Fragment
|
|
181
|
+
@ViewScoped // lives as long as View
|
|
182
|
+
@ServiceScoped // lives as long as Service
|
|
183
|
+
|
|
184
|
+
// ✅ Example — screen-level scope
|
|
185
|
+
@Module
|
|
186
|
+
@InstallIn(ActivityRetainedComponent::class)
|
|
187
|
+
object CartModule {
|
|
188
|
+
|
|
189
|
+
@Provides
|
|
190
|
+
@ActivityRetainedScoped
|
|
191
|
+
fun provideCart(): ShoppingCart = ShoppingCart()
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Qualifiers
|
|
198
|
+
|
|
199
|
+
```kotlin
|
|
200
|
+
// ✅ Distinguish multiple bindings of the same type
|
|
201
|
+
@Qualifier
|
|
202
|
+
@Retention(AnnotationRetention.BINARY)
|
|
203
|
+
annotation class AuthInterceptorOkHttpClient
|
|
204
|
+
|
|
205
|
+
@Qualifier
|
|
206
|
+
@Retention(AnnotationRetention.BINARY)
|
|
207
|
+
annotation class LoggingInterceptorOkHttpClient
|
|
208
|
+
|
|
209
|
+
@Module
|
|
210
|
+
@InstallIn(SingletonComponent::class)
|
|
211
|
+
object NetworkModule {
|
|
212
|
+
|
|
213
|
+
@Provides
|
|
214
|
+
@Singleton
|
|
215
|
+
@AuthInterceptorOkHttpClient
|
|
216
|
+
fun provideAuthOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient =
|
|
217
|
+
OkHttpClient.Builder()
|
|
218
|
+
.addInterceptor(authInterceptor)
|
|
219
|
+
.build()
|
|
220
|
+
|
|
221
|
+
@Provides
|
|
222
|
+
@Singleton
|
|
223
|
+
@LoggingInterceptorOkHttpClient
|
|
224
|
+
fun provideLoggingOkHttpClient(): OkHttpClient =
|
|
225
|
+
OkHttpClient.Builder()
|
|
226
|
+
.addInterceptor(HttpLoggingInterceptor())
|
|
227
|
+
.build()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ✅ Using qualifier at injection site
|
|
231
|
+
class UserRepository @Inject constructor(
|
|
232
|
+
@AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Injecting Into Android Components
|
|
239
|
+
|
|
240
|
+
```kotlin
|
|
241
|
+
// ✅ Activity
|
|
242
|
+
@AndroidEntryPoint
|
|
243
|
+
class MainActivity : ComponentActivity() {
|
|
244
|
+
// Field injection (only when constructor injection is impossible)
|
|
245
|
+
@Inject lateinit var analytics: Analytics
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ✅ Fragment
|
|
249
|
+
@AndroidEntryPoint
|
|
250
|
+
class UserFragment : Fragment() {
|
|
251
|
+
private val viewModel: UserViewModel by viewModels()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ✅ Compose — hiltViewModel()
|
|
255
|
+
@Composable
|
|
256
|
+
fun UserScreen(
|
|
257
|
+
viewModel: UserViewModel = hiltViewModel()
|
|
258
|
+
) { ... }
|
|
259
|
+
|
|
260
|
+
// ✅ WorkManager
|
|
261
|
+
@HiltWorker
|
|
262
|
+
class SyncWorker @AssistedInject constructor(
|
|
263
|
+
@Assisted context: Context,
|
|
264
|
+
@Assisted params: WorkerParameters,
|
|
265
|
+
private val syncEngine: SyncEngine
|
|
266
|
+
) : CoroutineWorker(context, params) {
|
|
267
|
+
override suspend fun doWork(): Result = TODO()
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Entry Points (Non-Hilt Classes)
|
|
274
|
+
|
|
275
|
+
```kotlin
|
|
276
|
+
// ✅ Access Hilt graph from non-Hilt class (e.g., ContentProvider, custom View)
|
|
277
|
+
@EntryPoint
|
|
278
|
+
@InstallIn(SingletonComponent::class)
|
|
279
|
+
interface UserRepositoryEntryPoint {
|
|
280
|
+
fun userRepository(): UserRepository
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Usage
|
|
284
|
+
val entryPoint = EntryPointAccessors.fromApplication(
|
|
285
|
+
context.applicationContext,
|
|
286
|
+
UserRepositoryEntryPoint::class.java
|
|
287
|
+
)
|
|
288
|
+
val repository = entryPoint.userRepository()
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Testing with Hilt
|
|
294
|
+
|
|
295
|
+
```kotlin
|
|
296
|
+
// ✅ Replace module in tests
|
|
297
|
+
@HiltAndroidTest
|
|
298
|
+
class UserRepositoryTest {
|
|
299
|
+
|
|
300
|
+
@get:Rule
|
|
301
|
+
val hiltRule = HiltAndroidRule(this)
|
|
302
|
+
|
|
303
|
+
@Inject
|
|
304
|
+
lateinit var repository: UserRepository
|
|
305
|
+
|
|
306
|
+
@Before
|
|
307
|
+
fun setUp() { hiltRule.inject() }
|
|
308
|
+
|
|
309
|
+
@Test
|
|
310
|
+
fun testGetUsers() { TODO() }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ✅ Replace a binding for tests
|
|
314
|
+
@UninstallModules(NetworkModule::class)
|
|
315
|
+
@HiltAndroidTest
|
|
316
|
+
class FakeNetworkTest {
|
|
317
|
+
|
|
318
|
+
@Module
|
|
319
|
+
@InstallIn(SingletonComponent::class)
|
|
320
|
+
object FakeNetworkModule {
|
|
321
|
+
@Provides
|
|
322
|
+
fun provideUserApi(): UserApi = FakeUserApi()
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Anti-Patterns
|
|
330
|
+
|
|
331
|
+
- Field injection (`@Inject lateinit var`) in non-Android classes — use constructor injection
|
|
332
|
+
- `@Singleton` on everything — wastes memory, creates hidden shared state
|
|
333
|
+
- Putting business logic in Hilt modules — modules only provide/bind, no logic
|
|
334
|
+
- Injecting `Context` directly — use `@ApplicationContext` or `@ActivityContext`
|
|
335
|
+
- Using `ServiceLocator` pattern alongside Hilt — pick one
|
|
336
|
+
- Circular dependencies — redesign with interfaces or lazy injection
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Related Skills
|
|
341
|
+
- `dagger` — Dagger internals when Hilt isn't sufficient
|
|
342
|
+
- `koin` — alternative DI framework
|
|
343
|
+
- `annotation-processing` — KSP setup for Hilt code generation
|
|
344
|
+
- `viewmodel` — @HiltViewModel pattern
|
|
345
|
+
- `workmanager` — @HiltWorker setup
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: koin
|
|
3
|
+
description: >
|
|
4
|
+
Koin dependency injection setup and patterns for Android and KMP.
|
|
5
|
+
Load this skill when working on a project that uses Koin, setting up
|
|
6
|
+
modules, injecting into Android components, or using Koin in KMP projects.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Koin
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Koin is a lightweight DI framework for Kotlin. Unlike Dagger/Hilt, Koin is a service locator at runtime — no code generation, no annotation processing. It's simpler to set up and KMP-compatible. The tradeoff is runtime resolution errors instead of compile-time errors.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- Koin resolves dependencies at **runtime** — errors surface at app start, not compile time
|
|
19
|
+
- Prefer Hilt for pure Android projects — Koin shines in **KMP** where Hilt isn't available
|
|
20
|
+
- Define dependencies in **modules** — one module per feature or layer
|
|
21
|
+
- Use `single` for singletons, `factory` for new instances, `viewModel` for ViewModels
|
|
22
|
+
- Always run `checkModules()` in tests — catches missing bindings before production
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
[versions]
|
|
30
|
+
koin = "3.5.6"
|
|
31
|
+
koin-compose = "3.5.6"
|
|
32
|
+
|
|
33
|
+
[libraries]
|
|
34
|
+
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
|
35
|
+
koin-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin-compose" }
|
|
36
|
+
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } # KMP
|
|
37
|
+
koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```kotlin
|
|
41
|
+
// build.gradle.kts
|
|
42
|
+
dependencies {
|
|
43
|
+
implementation(libs.koin.android)
|
|
44
|
+
implementation(libs.koin.compose)
|
|
45
|
+
testImplementation(libs.koin.test)
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Koin Application Setup
|
|
52
|
+
|
|
53
|
+
```kotlin
|
|
54
|
+
// ✅ Initialize in Application
|
|
55
|
+
class MyApplication : Application() {
|
|
56
|
+
override fun onCreate() {
|
|
57
|
+
super.onCreate()
|
|
58
|
+
startKoin {
|
|
59
|
+
androidLogger(Level.DEBUG)
|
|
60
|
+
androidContext(this@MyApplication)
|
|
61
|
+
modules(
|
|
62
|
+
networkModule,
|
|
63
|
+
databaseModule,
|
|
64
|
+
repositoryModule,
|
|
65
|
+
viewModelModule
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Defining Modules
|
|
75
|
+
|
|
76
|
+
```kotlin
|
|
77
|
+
// ✅ Network module
|
|
78
|
+
val networkModule = module {
|
|
79
|
+
|
|
80
|
+
single {
|
|
81
|
+
OkHttpClient.Builder()
|
|
82
|
+
.connectTimeout(30, TimeUnit.SECONDS)
|
|
83
|
+
.build()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
single {
|
|
87
|
+
Retrofit.Builder()
|
|
88
|
+
.baseUrl("https://api.example.com/")
|
|
89
|
+
.client(get()) // get() resolves OkHttpClient
|
|
90
|
+
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
|
|
91
|
+
.build()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
single<UserApi> { get<Retrofit>().create(UserApi::class.java) }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ✅ Database module
|
|
98
|
+
val databaseModule = module {
|
|
99
|
+
|
|
100
|
+
single {
|
|
101
|
+
Room.databaseBuilder(androidContext(), AppDatabase::class.java, "app_db")
|
|
102
|
+
.addMigrations(MIGRATION_1_2)
|
|
103
|
+
.build()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
single { get<AppDatabase>().userDao() }
|
|
107
|
+
single { get<AppDatabase>().orderDao() }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ✅ Repository module
|
|
111
|
+
val repositoryModule = module {
|
|
112
|
+
single<UserRepository> { UserRepositoryImpl(get(), get(), get()) }
|
|
113
|
+
single { UserMapper() }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ✅ ViewModel module
|
|
117
|
+
val viewModelModule = module {
|
|
118
|
+
viewModel { UserListViewModel(get()) }
|
|
119
|
+
viewModel { parameters -> UserDetailViewModel(get(), parameters.get()) }
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Scopes
|
|
126
|
+
|
|
127
|
+
```kotlin
|
|
128
|
+
// single — one instance for the entire app lifetime
|
|
129
|
+
// factory — new instance every time it's requested
|
|
130
|
+
// scoped — one instance per defined scope lifetime
|
|
131
|
+
|
|
132
|
+
val sessionModule = module {
|
|
133
|
+
|
|
134
|
+
// ✅ Scoped to a session
|
|
135
|
+
scope<UserSession> {
|
|
136
|
+
scoped { CartRepository(get()) }
|
|
137
|
+
scoped { CheckoutViewModel(get()) }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ✅ Create and close scope
|
|
142
|
+
val scope = getKoin().createScope("session_1", named<UserSession>())
|
|
143
|
+
val cart = scope.get<CartRepository>()
|
|
144
|
+
scope.close() // releases scoped instances
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Injection in Android Components
|
|
150
|
+
|
|
151
|
+
```kotlin
|
|
152
|
+
// ✅ Activity / Fragment — by inject()
|
|
153
|
+
class UserFragment : Fragment() {
|
|
154
|
+
private val viewModel: UserListViewModel by viewModel()
|
|
155
|
+
private val repository: UserRepository by inject()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ✅ Compose — koinViewModel()
|
|
159
|
+
@Composable
|
|
160
|
+
fun UserScreen(
|
|
161
|
+
viewModel: UserListViewModel = koinViewModel()
|
|
162
|
+
) { ... }
|
|
163
|
+
|
|
164
|
+
// ✅ ViewModel with parameters
|
|
165
|
+
@Composable
|
|
166
|
+
fun UserDetailScreen(userId: String) {
|
|
167
|
+
val viewModel: UserDetailViewModel = koinViewModel(
|
|
168
|
+
parameters = { parametersOf(userId) }
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Qualifiers
|
|
176
|
+
|
|
177
|
+
```kotlin
|
|
178
|
+
// ✅ Named bindings — distinguish same type
|
|
179
|
+
val networkModule = module {
|
|
180
|
+
single(named("auth")) {
|
|
181
|
+
OkHttpClient.Builder()
|
|
182
|
+
.addInterceptor(get<AuthInterceptor>())
|
|
183
|
+
.build()
|
|
184
|
+
}
|
|
185
|
+
single(named("logging")) {
|
|
186
|
+
OkHttpClient.Builder()
|
|
187
|
+
.addInterceptor(HttpLoggingInterceptor())
|
|
188
|
+
.build()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ✅ Inject with qualifier
|
|
193
|
+
class UserRepository(
|
|
194
|
+
private val client: OkHttpClient = get(named("auth"))
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## KMP Setup (commonMain)
|
|
201
|
+
|
|
202
|
+
```kotlin
|
|
203
|
+
// ✅ Koin works in commonMain — no Android dependency
|
|
204
|
+
// commonMain
|
|
205
|
+
val sharedModule = module {
|
|
206
|
+
single { UserRepository(get(), get()) }
|
|
207
|
+
single { UserMapper() }
|
|
208
|
+
single { HttpClient { /* Ktor config */ } }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// androidMain
|
|
212
|
+
val androidModule = module {
|
|
213
|
+
single { AndroidLogger() }
|
|
214
|
+
viewModel { UserListViewModel(get()) }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// iosMain — use Koin in Swift via KMP
|
|
218
|
+
// val iosModule = module { ... }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Testing
|
|
224
|
+
|
|
225
|
+
```kotlin
|
|
226
|
+
// ✅ checkModules — verify all bindings resolve
|
|
227
|
+
class KoinModuleTest : KoinTest {
|
|
228
|
+
|
|
229
|
+
@Test
|
|
230
|
+
fun verifyKoinModules() {
|
|
231
|
+
checkModules {
|
|
232
|
+
modules(networkModule, databaseModule, repositoryModule, viewModelModule)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ✅ Override bindings in tests
|
|
238
|
+
@Test
|
|
239
|
+
fun testWithFakeDependency() {
|
|
240
|
+
startKoin {
|
|
241
|
+
modules(
|
|
242
|
+
module {
|
|
243
|
+
single<UserRepository> { FakeUserRepository() }
|
|
244
|
+
viewModel { UserListViewModel(get()) }
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
// test...
|
|
249
|
+
stopKoin()
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Koin vs Hilt
|
|
256
|
+
|
|
257
|
+
| | Koin | Hilt |
|
|
258
|
+
|--|------|------|
|
|
259
|
+
| Error detection | Runtime | Compile-time |
|
|
260
|
+
| Setup complexity | Low | Medium |
|
|
261
|
+
| KMP support | ✅ Yes | ❌ No |
|
|
262
|
+
| Code generation | ❌ No | ✅ Yes |
|
|
263
|
+
| Android integration | Good | Excellent |
|
|
264
|
+
| Recommended for | KMP, simple apps | Android-only |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Anti-Patterns
|
|
269
|
+
|
|
270
|
+
- Not calling `checkModules()` in tests — runtime errors reach production
|
|
271
|
+
- Using `inject()` (lazy) when `get()` (eager) is needed for initialization order
|
|
272
|
+
- Defining all bindings in one giant module — split by feature/layer
|
|
273
|
+
- Using `single` for everything — `factory` for stateless dependencies
|
|
274
|
+
- Not closing scopes — memory leaks for scoped dependencies
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Related Skills
|
|
279
|
+
- `hilt` — preferred DI for Android-only projects
|
|
280
|
+
- `dagger` — compile-time DI underlying Hilt
|
|
281
|
+
- `kmp` — Koin in multiplatform projects
|
|
282
|
+
- `viewmodel` — ViewModel injection with Koin
|