База данных в Android: SQLite и Room простыми словами
Короткий ответ: используйте Room для большинства проектов — это безопасная, типизированная надстройка над встроенным SQLite; SQLite применим для мини‑приложений или супер‑оптимизированных запросов. Ниже—практика: когда что брать, как быстро настроить Room и избежать типичных ошибок.
Что такое SQLite и когда его использовать
SQLite — это встроенная реляционная БД в Android, хранящаяся в одном файле (/data/data/…/databases/*.db). Подходит для офлайн‑данных (заметки, кэш, локальные настройки).
Плюсы:
- Лёгкая и быстрая, поддерживает SQL, транзакции и индексы.
- Минимальный размер APK‑прибавки.
Минусы:
- Ручной SQL = больше багов (опечатки, SQL‑инъекции, отсутствие type‑safety).
- Нужно самому управлять миграциями, потоками и закрытием курсоров.
Частая ошибка: доступ к БД из main thread и незакрытые курсоры. Это ведёт к крашам и утечкам памяти.
Когда взять SQLite напрямую:
- Нужны сверхоптимизированные, специфичные SQL‑операции.
- Очень маленький прототип/однократное приложение. Во всех остальных случаях — Room.
Room: зачем и как подключить (шаги)
Room — часть Jetpack: генерирует DAO/Entity/Database, даёт compile‑time проверки, поддерживает Coroutines, Flow, LiveData и миграции.
- Добавьте зависимости (пример для Gradle Kotlin DSL):
val roomVersion = "2.6.1"
implementation("androidx.room:room-runtime:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion") // или ksp для KSP
- Entity — модель таблицы:
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name = "full_name") val name: String
)
- DAO — интерфейс операций:
@Dao
interface UserDao {
@Query("SELECT * FROM users")
suspend fun getAll(): List<User>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(user: User)
@Delete
suspend fun delete(user: User)
}
- Database — корень:
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Создание экземпляра:
val db = Room.databaseBuilder(context, AppDatabase::class.java, "app-db").build()
Room упрощает работу с потоками (suspend / Flow / LiveData), миграциями и тестированием.
Для UI используйте Flow или LiveData — они автоматически обновляют экран при изменении данных. exportSchema = true поможет сохранять файлы схемы для контроля миграций.
Практический пример: TODO‑приложение (минимум кода)
Entity:
@Entity
data class Task(
@PrimaryKey val id: String = UUID.randomUUID().toString(),
val title: String,
val isDone: Boolean = false,
@ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis()
)
DAO с Flow:
@Dao
interface TaskDao {
@Query("SELECT * FROM Task WHERE isDone = 0 ORDER BY created_at DESC")
fun getPendingTasks(): Flow<List<Task>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(task: Task)
}
ViewModel:
class TaskViewModel(private val dao: TaskDao) : ViewModel() {
val pendingTasks = dao.getPendingTasks().asLiveData()
fun addTask(title: String) = viewModelScope.launch { dao.insert(Task(title = title)) }
}
Проверьте базу в эмуляторе через Device File Explorer: файл появится и данные будут сохраняться.
Сравнение в двух строчках
- SQLite напрямую: меньше зависимостей, но больше boilerplate и рисков.
- Room: +type‑safety, +меньше багов, +поддержка Coroutines/Flow — выбор для 90% проектов.
Частые ошибки
- Доступ к БД из main thread → используйте suspend/Dispatchers.IO.
- Забыл добавить миграцию при повышении версии → приложение может крашнуть.
- exportSchema = false в проде → потеря контроля над схемами.
- Неправильный scope для Room instance → держите single‑ton или DI‑провайдер.
- Плохие индексы → медленные WHERE/ORDER BY запросы.
FAQ
- Нужен ли Room для маленького приложения?
- Для простого прототипа можно обойтись SQLite, но Room экономит время и снижает баги даже в маленьких проектах.
- Как тестировать Room?
- Используйте Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) для unit‑тестов.
- Что делать с миграциями в проде?
- Писать Migration объекты и тестировать; fallbackToDestructiveMigration только для разработки или осознанного поведения.
- Kapt или KSP?
- На новых проектах рекомендуют KSP (меньше времени компиляции), но KAPT по‑прежнему поддерживается.
Удачи: начните с Room, добавьте индексы для частых запросов и используйте Flow/LiveData — это даст стабильность и удобство разработки.