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,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: room
|
|
3
|
+
description: >
|
|
4
|
+
Room database setup, configuration, and best practices for Android.
|
|
5
|
+
Load this skill when setting up Room, defining entities, configuring
|
|
6
|
+
the database class, handling migrations, or integrating Room with
|
|
7
|
+
repositories and coroutines/Flow.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Room
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
Room is Android's SQLite abstraction library. It provides compile-time SQL verification, coroutine/Flow support, and migration tooling. Room is the standard local database solution for Android and works as the offline-first data source in a Repository pattern.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- Room operations must always run on **Dispatchers.IO** — never main thread
|
|
20
|
+
- Expose **Flow** from DAOs for observable queries — never suspend for reactive data
|
|
21
|
+
- Use **suspend** for one-shot write operations (insert, update, delete)
|
|
22
|
+
- Never expose entities outside the data layer — map to domain models in Repository
|
|
23
|
+
- Always define **indices** on columns used in WHERE and JOIN clauses
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```toml
|
|
30
|
+
# libs.versions.toml
|
|
31
|
+
[versions]
|
|
32
|
+
room = "2.6.1"
|
|
33
|
+
|
|
34
|
+
[libraries]
|
|
35
|
+
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
|
36
|
+
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
|
37
|
+
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
|
38
|
+
|
|
39
|
+
[plugins]
|
|
40
|
+
ksp = { id = "com.google.devtools.ksp", version = "2.0.0-1.0.21" }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```kotlin
|
|
44
|
+
// build.gradle.kts
|
|
45
|
+
plugins {
|
|
46
|
+
alias(libs.plugins.ksp)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
dependencies {
|
|
50
|
+
implementation(libs.room.runtime)
|
|
51
|
+
implementation(libs.room.ktx)
|
|
52
|
+
ksp(libs.room.compiler)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ✅ Schema export — for migration verification
|
|
56
|
+
ksp {
|
|
57
|
+
arg("room.schemaLocation", "$projectDir/schemas")
|
|
58
|
+
arg("room.incremental", "true")
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Entity
|
|
65
|
+
|
|
66
|
+
```kotlin
|
|
67
|
+
// ✅ Entity — maps to a database table
|
|
68
|
+
@Entity(
|
|
69
|
+
tableName = "users",
|
|
70
|
+
indices = [
|
|
71
|
+
Index(value = ["email"], unique = true),
|
|
72
|
+
Index(value = ["status"])
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
data class UserEntity(
|
|
76
|
+
@PrimaryKey val id: String,
|
|
77
|
+
@ColumnInfo(name = "full_name") val name: String,
|
|
78
|
+
@ColumnInfo(name = "email") val email: String,
|
|
79
|
+
@ColumnInfo(name = "status") val status: String,
|
|
80
|
+
@ColumnInfo(name = "created_at") val createdAt: Long,
|
|
81
|
+
@ColumnInfo(name = "updated_at") val updatedAt: Long
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// ✅ Embedded object
|
|
85
|
+
data class Address(
|
|
86
|
+
val street: String,
|
|
87
|
+
val city: String,
|
|
88
|
+
val country: String
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@Entity(tableName = "users")
|
|
92
|
+
data class UserEntity(
|
|
93
|
+
@PrimaryKey val id: String,
|
|
94
|
+
val name: String,
|
|
95
|
+
@Embedded val address: Address
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// ✅ Relation — one-to-many
|
|
99
|
+
data class UserWithOrders(
|
|
100
|
+
@Embedded val user: UserEntity,
|
|
101
|
+
@Relation(
|
|
102
|
+
parentColumn = "id",
|
|
103
|
+
entityColumn = "user_id"
|
|
104
|
+
)
|
|
105
|
+
val orders: List<OrderEntity>
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Database Class
|
|
112
|
+
|
|
113
|
+
```kotlin
|
|
114
|
+
// ✅ Database definition
|
|
115
|
+
@Database(
|
|
116
|
+
entities = [
|
|
117
|
+
UserEntity::class,
|
|
118
|
+
OrderEntity::class,
|
|
119
|
+
ProductEntity::class
|
|
120
|
+
],
|
|
121
|
+
version = 3,
|
|
122
|
+
exportSchema = true // always true — needed for migration testing
|
|
123
|
+
)
|
|
124
|
+
@TypeConverters(Converters::class)
|
|
125
|
+
abstract class AppDatabase : RoomDatabase() {
|
|
126
|
+
|
|
127
|
+
abstract fun userDao(): UserDao
|
|
128
|
+
abstract fun orderDao(): OrderDao
|
|
129
|
+
abstract fun productDao(): ProductDao
|
|
130
|
+
|
|
131
|
+
companion object {
|
|
132
|
+
@Volatile
|
|
133
|
+
private var INSTANCE: AppDatabase? = null
|
|
134
|
+
|
|
135
|
+
fun getInstance(context: Context): AppDatabase {
|
|
136
|
+
return INSTANCE ?: synchronized(this) {
|
|
137
|
+
Room.databaseBuilder(
|
|
138
|
+
context.applicationContext,
|
|
139
|
+
AppDatabase::class.java,
|
|
140
|
+
"app_database"
|
|
141
|
+
)
|
|
142
|
+
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
|
|
143
|
+
.fallbackToDestructiveMigrationOnDowngrade()
|
|
144
|
+
.build()
|
|
145
|
+
.also { INSTANCE = it }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ✅ With Hilt — preferred over manual singleton
|
|
152
|
+
@Module
|
|
153
|
+
@InstallIn(SingletonComponent::class)
|
|
154
|
+
object DatabaseModule {
|
|
155
|
+
|
|
156
|
+
@Provides
|
|
157
|
+
@Singleton
|
|
158
|
+
fun provideDatabase(@ApplicationContext context: Context): AppDatabase =
|
|
159
|
+
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
|
|
160
|
+
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
|
|
161
|
+
.build()
|
|
162
|
+
|
|
163
|
+
@Provides
|
|
164
|
+
fun provideUserDao(database: AppDatabase): UserDao = database.userDao()
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Type Converters
|
|
171
|
+
|
|
172
|
+
```kotlin
|
|
173
|
+
// ✅ For types Room can't store natively
|
|
174
|
+
class Converters {
|
|
175
|
+
|
|
176
|
+
@TypeConverter
|
|
177
|
+
fun fromTimestamp(value: Long?): Date? =
|
|
178
|
+
value?.let { Date(it) }
|
|
179
|
+
|
|
180
|
+
@TypeConverter
|
|
181
|
+
fun dateToTimestamp(date: Date?): Long? =
|
|
182
|
+
date?.time
|
|
183
|
+
|
|
184
|
+
@TypeConverter
|
|
185
|
+
fun fromStringList(value: String): List<String> =
|
|
186
|
+
Json.decodeFromString(value)
|
|
187
|
+
|
|
188
|
+
@TypeConverter
|
|
189
|
+
fun toStringList(list: List<String>): String =
|
|
190
|
+
Json.encodeToString(list)
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Integration with Repository
|
|
197
|
+
|
|
198
|
+
```kotlin
|
|
199
|
+
// ✅ Repository maps Entity ↔ Domain model
|
|
200
|
+
class UserRepository @Inject constructor(
|
|
201
|
+
private val userDao: UserDao,
|
|
202
|
+
private val userMapper: UserMapper
|
|
203
|
+
) {
|
|
204
|
+
|
|
205
|
+
// Observable query — Flow
|
|
206
|
+
fun observeUsers(): Flow<List<User>> =
|
|
207
|
+
userDao.observeAll()
|
|
208
|
+
.map { entities -> entities.map { userMapper.toDomain(it) } }
|
|
209
|
+
|
|
210
|
+
// One-shot read
|
|
211
|
+
suspend fun getUserById(id: String): User? =
|
|
212
|
+
userDao.getById(id)?.let { userMapper.toDomain(it) }
|
|
213
|
+
|
|
214
|
+
// Write operations
|
|
215
|
+
suspend fun saveUser(user: User) {
|
|
216
|
+
userDao.upsert(userMapper.toEntity(user))
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
suspend fun deleteUser(id: String) {
|
|
220
|
+
userDao.deleteById(id)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Anti-Patterns
|
|
228
|
+
|
|
229
|
+
- Running Room queries on the main thread — causes ANR
|
|
230
|
+
- Exposing `UserEntity` outside the data layer — couples UI to DB schema
|
|
231
|
+
- Using `allowMainThreadQueries()` — only acceptable in tests
|
|
232
|
+
- Not exporting schema (`exportSchema = false`) — can't write or test migrations
|
|
233
|
+
- Missing indices on queried columns — slow queries on large datasets
|
|
234
|
+
- Using `@PrimaryKey(autoGenerate = true)` with String IDs — use UUID instead
|
|
235
|
+
- Accessing database instance directly from ViewModel — always go through Repository
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Related Skills
|
|
240
|
+
- `dao` — DAO queries and operations
|
|
241
|
+
- `migration` — database schema migrations
|
|
242
|
+
- `dto-mapping` — Entity ↔ Domain mapping
|
|
243
|
+
- `repository-pattern` — Repository wrapping Room
|
|
244
|
+
- `hilt` — injecting Room database and DAOs
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sqlite
|
|
3
|
+
description: >
|
|
4
|
+
SQLite fundamentals and direct SQLite usage in Android.
|
|
5
|
+
Load this skill when working with raw SQLite queries, SupportSQLiteDatabase
|
|
6
|
+
in migrations, understanding SQLite constraints, or diagnosing SQL issues
|
|
7
|
+
that go beyond Room's abstraction.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# SQLite
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
SQLite is the embedded relational database engine used by Android. Room is built on top of SQLite and handles most use cases. Direct SQLite access is needed in migrations, performance diagnostics, and edge cases that Room doesn't cover. Understanding SQLite's constraints prevents data integrity bugs.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- Prefer Room over raw SQLite — use raw SQLite only when Room is insufficient
|
|
20
|
+
- SQLite is **not thread-safe** — always use Room's dispatcher management
|
|
21
|
+
- SQLite uses **dynamic typing** — enforce types via constraints, not the engine
|
|
22
|
+
- SQLite does **not enforce foreign keys by default** — must be enabled explicitly
|
|
23
|
+
- Transactions are the primary tool for performance and atomicity
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## SQLite Type System
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
-- SQLite storage classes (not strict types)
|
|
31
|
+
NULL -- null value
|
|
32
|
+
INTEGER -- signed integer (1, 2, 3, 4, 6, or 8 bytes)
|
|
33
|
+
REAL -- floating point (8-byte IEEE 754)
|
|
34
|
+
TEXT -- UTF-8/16 string
|
|
35
|
+
BLOB -- binary data
|
|
36
|
+
|
|
37
|
+
-- Room maps Kotlin types to SQLite:
|
|
38
|
+
-- String → TEXT
|
|
39
|
+
-- Int → INTEGER
|
|
40
|
+
-- Long → INTEGER
|
|
41
|
+
-- Double → REAL
|
|
42
|
+
-- Boolean → INTEGER (0 or 1)
|
|
43
|
+
-- ByteArray → BLOB
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Foreign Keys — Must Be Enabled
|
|
49
|
+
|
|
50
|
+
```kotlin
|
|
51
|
+
// ✅ Enable foreign key enforcement in Room
|
|
52
|
+
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
|
|
53
|
+
.addCallback(object : RoomDatabase.Callback() {
|
|
54
|
+
override fun onOpen(db: SupportSQLiteDatabase) {
|
|
55
|
+
db.execSQL("PRAGMA foreign_keys = ON")
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
.build()
|
|
59
|
+
|
|
60
|
+
// Without this, foreign key constraints are silently ignored
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Direct SQL in Migrations
|
|
66
|
+
|
|
67
|
+
```kotlin
|
|
68
|
+
val MIGRATION_3_4 = object : Migration(3, 4) {
|
|
69
|
+
override fun migrate(database: SupportSQLiteDatabase) {
|
|
70
|
+
|
|
71
|
+
// ✅ Create table
|
|
72
|
+
database.execSQL("""
|
|
73
|
+
CREATE TABLE IF NOT EXISTS products (
|
|
74
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
75
|
+
name TEXT NOT NULL,
|
|
76
|
+
price INTEGER NOT NULL DEFAULT 0,
|
|
77
|
+
category TEXT NOT NULL,
|
|
78
|
+
created_at INTEGER NOT NULL
|
|
79
|
+
)
|
|
80
|
+
""")
|
|
81
|
+
|
|
82
|
+
// ✅ Add column
|
|
83
|
+
database.execSQL("ALTER TABLE users ADD COLUMN avatar_url TEXT")
|
|
84
|
+
|
|
85
|
+
// ✅ Create index
|
|
86
|
+
database.execSQL(
|
|
87
|
+
"CREATE INDEX IF NOT EXISTS index_products_category ON products(category)"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
// ✅ Create unique index
|
|
91
|
+
database.execSQL(
|
|
92
|
+
"CREATE UNIQUE INDEX IF NOT EXISTS index_users_email ON users(email)"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
// ✅ Drop index
|
|
96
|
+
database.execSQL("DROP INDEX IF EXISTS old_index_name")
|
|
97
|
+
|
|
98
|
+
// ✅ Drop table
|
|
99
|
+
database.execSQL("DROP TABLE IF EXISTS old_table")
|
|
100
|
+
|
|
101
|
+
// ✅ Rename table (within migration)
|
|
102
|
+
database.execSQL("ALTER TABLE users_temp RENAME TO users")
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Rename Column (Pre-API 29)
|
|
110
|
+
|
|
111
|
+
```kotlin
|
|
112
|
+
// SQLite supports RENAME COLUMN only from API 29 / SQLite 3.25.0
|
|
113
|
+
// For older support — recreate table
|
|
114
|
+
|
|
115
|
+
val MIGRATION_RENAME = object : Migration(5, 6) {
|
|
116
|
+
override fun migrate(database: SupportSQLiteDatabase) {
|
|
117
|
+
// 1. Create new table with correct schema
|
|
118
|
+
database.execSQL("""
|
|
119
|
+
CREATE TABLE users_new (
|
|
120
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
121
|
+
full_name TEXT NOT NULL, -- renamed from 'name'
|
|
122
|
+
email TEXT NOT NULL,
|
|
123
|
+
created_at INTEGER NOT NULL
|
|
124
|
+
)
|
|
125
|
+
""")
|
|
126
|
+
// 2. Copy data
|
|
127
|
+
database.execSQL("""
|
|
128
|
+
INSERT INTO users_new (id, full_name, email, created_at)
|
|
129
|
+
SELECT id, name, email, created_at FROM users
|
|
130
|
+
""")
|
|
131
|
+
// 3. Drop old table
|
|
132
|
+
database.execSQL("DROP TABLE users")
|
|
133
|
+
// 4. Rename new table
|
|
134
|
+
database.execSQL("ALTER TABLE users_new RENAME TO users")
|
|
135
|
+
// 5. Recreate indices
|
|
136
|
+
database.execSQL("CREATE INDEX index_users_email ON users(email)")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Transactions for Performance
|
|
144
|
+
|
|
145
|
+
```kotlin
|
|
146
|
+
// ✅ Wrap bulk inserts in a transaction — dramatically faster
|
|
147
|
+
val MIGRATION_BULK = object : Migration(6, 7) {
|
|
148
|
+
override fun migrate(database: SupportSQLiteDatabase) {
|
|
149
|
+
database.beginTransaction()
|
|
150
|
+
try {
|
|
151
|
+
database.execSQL("ALTER TABLE orders ADD COLUMN tax INTEGER NOT NULL DEFAULT 0")
|
|
152
|
+
// backfill
|
|
153
|
+
database.execSQL("UPDATE orders SET tax = CAST(total * 0.09 AS INTEGER)")
|
|
154
|
+
database.setTransactionSuccessful()
|
|
155
|
+
} finally {
|
|
156
|
+
database.endTransaction()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ✅ Room handles transactions via @Transaction in DAOs
|
|
162
|
+
// Use raw transactions only in migrations or SupportSQLiteDatabase contexts
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Useful PRAGMA Commands
|
|
168
|
+
|
|
169
|
+
```sql
|
|
170
|
+
-- Check schema integrity
|
|
171
|
+
PRAGMA integrity_check;
|
|
172
|
+
|
|
173
|
+
-- Get table info (columns, types, constraints)
|
|
174
|
+
PRAGMA table_info(users);
|
|
175
|
+
|
|
176
|
+
-- List all tables
|
|
177
|
+
SELECT name FROM sqlite_master WHERE type='table';
|
|
178
|
+
|
|
179
|
+
-- List all indices
|
|
180
|
+
SELECT name FROM sqlite_master WHERE type='index';
|
|
181
|
+
|
|
182
|
+
-- Get foreign key list for a table
|
|
183
|
+
PRAGMA foreign_key_list(orders);
|
|
184
|
+
|
|
185
|
+
-- Check foreign key violations
|
|
186
|
+
PRAGMA foreign_key_check;
|
|
187
|
+
|
|
188
|
+
-- Get current database version (Room version)
|
|
189
|
+
PRAGMA user_version;
|
|
190
|
+
|
|
191
|
+
-- WAL mode — better concurrent read performance
|
|
192
|
+
PRAGMA journal_mode = WAL;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## WAL Mode
|
|
198
|
+
|
|
199
|
+
```kotlin
|
|
200
|
+
// ✅ Enable WAL (Write-Ahead Logging) for better read concurrency
|
|
201
|
+
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
|
|
202
|
+
.setJournalMode(RoomDatabase.JournalMode.WRITE_AHEAD_LOGGING)
|
|
203
|
+
.build()
|
|
204
|
+
|
|
205
|
+
// WAL benefits:
|
|
206
|
+
// - Readers don't block writers
|
|
207
|
+
// - Writers don't block readers
|
|
208
|
+
// - Better performance for concurrent access patterns
|
|
209
|
+
// Default in Room — explicitly set if needed
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Querying System Tables
|
|
215
|
+
|
|
216
|
+
```kotlin
|
|
217
|
+
// ✅ Check if a table exists (useful in migrations)
|
|
218
|
+
fun SupportSQLiteDatabase.tableExists(tableName: String): Boolean {
|
|
219
|
+
val cursor = query(
|
|
220
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
|
221
|
+
arrayOf(tableName)
|
|
222
|
+
)
|
|
223
|
+
return cursor.use { it.count > 0 }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ✅ Check if a column exists (useful in migrations)
|
|
227
|
+
fun SupportSQLiteDatabase.columnExists(tableName: String, columnName: String): Boolean {
|
|
228
|
+
val cursor = query("PRAGMA table_info($tableName)", null)
|
|
229
|
+
return cursor.use { c ->
|
|
230
|
+
while (c.moveToNext()) {
|
|
231
|
+
if (c.getString(c.getColumnIndex("name")) == columnName) return true
|
|
232
|
+
}
|
|
233
|
+
false
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Anti-Patterns
|
|
241
|
+
|
|
242
|
+
- Raw SQLite without Room — loses type safety, compile-time checks, and Flow support
|
|
243
|
+
- Not enabling foreign keys (`PRAGMA foreign_keys = ON`) — constraints silently ignored
|
|
244
|
+
- String concatenation in SQL — use parameterized queries to prevent SQL injection
|
|
245
|
+
- Bulk operations outside transactions — orders of magnitude slower
|
|
246
|
+
- Using `AUTOINCREMENT` keyword — use `INTEGER PRIMARY KEY` instead (simpler, faster)
|
|
247
|
+
- Assuming column order is stable — always use column names, not positions
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Related Skills
|
|
252
|
+
- `room` — Room as the primary SQLite abstraction
|
|
253
|
+
- `dao` — Room DAO for type-safe queries
|
|
254
|
+
- `migration` — using SupportSQLiteDatabase in migrations
|
|
255
|
+
- `indexing` — index creation and management
|