Подключение Room и создание локального хранилища в Android Studio

Короткий ответ: добавьте зависимости room-runtime, room-ktx и kapt room-compiler; опишите @Entity, @Dao и @Database; создайте репозиторий поверх DAO; интегрируйте с ViewModel через Flow/корутины. Ниже — компактный, применимый шаг за шагом план.

Что такое Room и когда его использовать

Room — обёртка над SQLite из Android Jetpack: статическая проверка SQL, преобразование в Kotlin‑объекты, поддержка корутин/Flow, миграций и тестирования. Используйте Room для персистентного хранилища (заметки, кэш API, офлайн‑данные) и сложных запросов (фильтры, JOIN). Для временных, in‑memory кешей достаточно ViewModel/State.

Room проверяет SQL на этапе компиляции — ошибки в названиях таблиц/полей вы увидите при сборке.

Быстрая настройка проекта

  1. В build.gradle (модуль app) подключите (Kotlin KTS‑пример):
val roomVersion = "2.6.1"
implementation("androidx.room:room-runtime:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
  1. Включите плагин kotlin-kapt в плагинах.
  2. Sync Now.

Частая ошибка — забыть kotlin-kapt или room-compiler: аннотации не сгенерируют код и сборка упадёт.

Шаги реализации (практика)

  1. Entity — таблица:
@Entity(tableName = "notes")
data class NoteEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String,
  val createdAt: Long,
  val updatedAt: Long? = null,
  val isPinned: Boolean = false
)
  1. DAO — доступ к данным (Flow для наблюдения, suspend для операций):
@Dao
interface NoteDao {
  @Query("SELECT * FROM notes ORDER BY isPinned DESC, updatedAt DESC")
  fun getAllNotes(): Flow<List<NoteEntity>>

  @Insert(onConflict = OnConflictStrategy.REPLACE)
  suspend fun insertNote(note: NoteEntity): Long
  // update, delete, getById...
}
  1. Database — точка входа:
@Database(entities = [NoteEntity::class], version = 1, exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
  abstract fun noteDao(): NoteDao
  companion object {
    fun getInstance(context: Context) = Room.databaseBuilder(
      context.applicationContext,
      AppDatabase::class.java,
      "app_database.db"
    ).build()
  }
}
  1. Миграции: при изменении схемы увеличьте version и добавьте Migration, либо используйте fallbackToDestructiveMigration() только для прототипов.

  2. Репозиторий — инкапсулирует логику доступа:

class NoteRepository(private val dao: NoteDao) {
  fun observeNotes() = dao.getAllNotes()
  suspend fun createNote(...) = dao.insertNote(...)
}
  1. ViewModel: подписывайтесь на Flow в viewModelScope, обновляйте StateFlow/LiveData и запускайте DAO‑операции в корутинах.

Начните с минимальной схемы и простых миграций. Добавляйте TypeConverters только при необходимости (списки, LocalDateTime).

Интеграция, отладка и рабочие советы

  • Вызывайте suspend‑методы в IO‑контексте (viewModelScope.launch(Dispatchers.IO)) или используйте room-ktx (для suspend Room сам переключает поток).
  • Для отладки используйте Database Inspector в Android Studio (App Inspection).
  • Хранение файла: data/data//databases/app_database.db — приватно для приложения.

Если увеличить version без миграций и без fallbackToDestructiveMigration(), приложение упадёт при обновлении.

Частые ошибки

  • Забыт kotlin-kapt или room-compiler → сборка падает.
  • Выполнение тяжёлых запросов на главном потоке → ANR.
  • Игнорирование миграций → потеря данных или краш при обновлении.
  • Излишнее SELECT * — передавайте только нужные поля (проекции/DTO).

FAQ

  • Нужно ли всегда писать репозиторий? Рекомендуется: он упрощает замену источников данных и тестирование.
  • Как хранить даты? Как правило — Long (timestamp) или ISO‑строка + TypeConverter для LocalDateTime.
  • Можно ли использовать Room с Jetpack Compose? Да — подписывайтесь на StateFlow/LiveData и обновляйте UI реактивно.

Если нужно, могу прислать пример с @Relation, несколькими таблицами и примером миграции между версиями.