База данных в 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 и миграции.

  1. Добавьте зависимости (пример для 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
  1. Entity — модель таблицы:
@Entity(tableName = "users")
data class User(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  @ColumnInfo(name = "full_name") val name: String
)
  1. 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)
}
  1. 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 — это даст стабильность и удобство разработки.