Основы Android Kotlin 06.1: Создание базы данных Room,Основы Android Kotlin 06.1: Создание базы данных Room

Эта практическая работа входит в курс «Основы Android Kotlin». Вы получите максимальную пользу от этого курса, если будете выполнять практические работы последовательно. Все практические работы курса перечислены на целевой странице практической работы «Основы Android Kotlin» .

Введение

В большинстве приложений есть данные, которые необходимо сохранять даже после того, как пользователь закрывает приложение. Например, приложение может хранить плейлист, список игровых предметов, записи расходов и доходов, каталог созвездий или данные о сне за определенный период времени. Обычно для хранения постоянных данных используется база данных.

Room — это библиотека для работы с базами данных, входящая в состав Android Jetpack . Room берёт на себя многие задачи по настройке и конфигурированию базы данных и позволяет вашему приложению взаимодействовать с ней, используя обычные вызовы функций. По сути, Room — это уровень абстракции, работающий поверх базы данных SQLite. Терминология Room и синтаксис запросов для более сложных запросов соответствуют модели SQLite.

На рисунке ниже показано, как база данных Room вписывается в общую архитектуру, рекомендуемую в этом курсе.

Что вам уже следует знать

Вам должно быть знакомо:

  • Создание базового пользовательского интерфейса (UI) для приложения Android
  • Использование действий, фрагментов и представлений.
  • Навигация между фрагментами и использование Safe Args (плагина Gradle) для передачи данных между фрагментами.
  • Модели представлений, фабрики моделей представлений, а также LiveData и их наблюдатели. Эти темы, связанные с компонентами архитектуры, рассматриваются в предыдущей практической работе этого курса.
  • Базовые знания баз данных SQL и языка SQLite. Для быстрого ознакомления или освежения знаний см. SQLite Primer.

Чему вы научитесь

  • Как создать и взаимодействовать с базой данных Room для сохранения данных.
  • Как создать класс данных, определяющий таблицу в базе данных.
  • Как использовать объект доступа к данным (DAO) для сопоставления функций Kotlin с SQL-запросами.
  • Как проверить работоспособность вашей базы данных.

Что ты будешь делать?

  • Создайте базу данных Room с интерфейсом для данных о ночном сне.
  • Протестируйте базу данных, используя предоставленные тесты.

В этой лабораторной работе вы создадите часть приложения, работающую с базой данных, для отслеживания качества сна. Приложение использует базу данных для хранения данных о сне с течением времени.

Приложение имеет два экрана, представленных фрагментами, как показано на рисунке ниже.

На первом экране, показанном слева, есть кнопки для запуска и остановки отслеживания. На экране отображаются все данные о сне пользователя. Кнопка «Очистить» безвозвратно удаляет все данные, собранные приложением для пользователя.

Второй экран, показанный справа, предназначен для выбора оценки качества сна. В приложении оценка представлена в числовом виде. В целях разработки приложение отображает как значки лиц, так и их числовые эквиваленты.

Поток действий пользователя выглядит следующим образом:

  • Пользователь открывает приложение и видит экран отслеживания сна.
  • Пользователь нажимает кнопку «Старт» . Время начала фиксируется и отображается на экране. Кнопка «Старт» отключается, а кнопка «Стоп» активируется.
  • Пользователь нажимает кнопку «Стоп» . Записывается время окончания и открывается экран качества сна.
  • Пользователь выбирает значок качества сна. Экран закрывается, и на экране отслеживания отображаются время окончания сна и качество сна. Кнопка «Стоп» отключается, а кнопка «Старт» активируется. Приложение готово к следующей ночи.
  • Кнопка «Очистить» активна, когда в базе данных есть данные. При нажатии кнопки «Очистить » все данные пользователя удаляются без возможности восстановления — сообщение «Вы уверены?» не появляется.

Это приложение использует упрощённую архитектуру, как показано ниже в контексте полной архитектуры. Приложение использует только следующие компоненты:

  • Контроллер пользовательского интерфейса
  • Просмотреть модель и LiveData
  • База данных A Room

Шаг 1: Загрузите и запустите стартовое приложение.

  1. Загрузите приложение TrackMySleepQuality-Starter с GitHub.
  2. Соберите и запустите приложение. Приложение отображает пользовательский интерфейс фрагмента SleepTrackerFragment , но не отображает данные. Кнопки не реагируют на нажатия.

Шаг 2: Проверьте стартовое приложение

  1. Взгляните на файлы Gradle:
  • Файл проекта Gradle
    В файле build.gradle уровня проекта обратите внимание на переменные, задающие версии библиотек. Версии, используемые в стартовом приложении, хорошо сочетаются друг с другом и хорошо работают с этим приложением. К моменту завершения этой лабораторной работы Android Studio может предложить вам обновить некоторые версии. Решение о том, обновлять или оставить версии, имеющиеся в приложении, зависит от вас. Если вы столкнётесь со «странными» ошибками компиляции, попробуйте использовать комбинацию версий библиотек, используемую в конечном решении .
  • Файл модуля Gradle. Обратите внимание на предоставленные зависимости для всех библиотек Android Jetpack, включая Room , а также зависимости для сопрограмм.
  1. Взгляните на пакеты и пользовательский интерфейс. Приложение структурировано по функциональным возможностям. Пакет содержит файлы-заглушки, в которые вы будете добавлять код в ходе этой серии практических занятий.
  • Пакет database для всего кода , относящегося к базе данных Room .
  • Пакеты sleepquality и sleeptracker содержат фрагмент, модель представления и фабрику моделей представления для каждого экрана.
  1. Взгляните на файл Util.kt , содержащий функции для отображения данных о качестве сна. Часть кода закомментирована, поскольку ссылается на модель представления, которую вы создадите позже.
  2. Взгляните на папку androidTest ( SleepDatabaseTest.kt ). Этот тест поможет вам проверить, работает ли база данных должным образом.

В Android данные представлены в классах данных, а доступ к ним и их изменение осуществляются с помощью вызовов функций. Однако в мире баз данных необходимы сущности и запросы .

  • Сущность представляет собой объект или понятие и его свойства, хранящиеся в базе данных. Класс сущности определяет таблицу, и каждый экземпляр этого класса представляет строку в таблице. Каждое свойство определяет столбец. В вашем приложении сущность будет хранить информацию о сне за ночь.
  • Запрос — это запрос данных или информации из таблицы базы данных или комбинации таблиц, а также запрос на выполнение действия с данными. Обычно запросы предназначены для получения, добавления и обновления сущностей. Например, можно запросить все зарегистрированные ночи сна, отсортированные по времени начала.

Room выполняет всю сложную работу по переходу от классов данных Kotlin к сущностям, которые можно хранить в таблицах SQLite, и от объявлений функций к запросам SQL.

Необходимо определить каждую сущность как аннотированный класс данных, а взаимодействия — как аннотированный интерфейс, объект доступа к данным (DAO) . Room использует эти аннотированные классы для создания таблиц в базе данных и запросов к ней.

Шаг 1: Создание объекта SleepNight

В этой задаче вы определяете одну ночь сна как аннотированный класс данных.

Для одной ночи сна вам необходимо записать время начала, время окончания и оценку качества.

И вам понадобится идентификатор, чтобы однозначно идентифицировать ночь.

  1. В пакете database найдите и откройте файл SleepNight.kt .
  2. Создайте класс данных SleepNight с параметрами для идентификатора, времени начала (в миллисекундах), времени окончания (в миллисекундах) и числовой оценки качества сна.
  • Вам необходимо инициализировать sleepQuality , установив его на -1 , что будет означать, что данные о качестве не были собраны.
  • Вам также необходимо инициализировать время окончания. Установите его равным времени начала, чтобы обозначить, что время окончания ещё не записано.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. Перед объявлением класса добавьте аннотацию @Entity к классу данных. Назовите таблицу daily_sleep_quality_table . Аргумент tableName необязателен, но рекомендуется. Информация о других аргументах представлена в документации.

    При появлении запроса импортируйте Entity и все остальные аннотации из библиотеки androidx .
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. Чтобы определить nightId как первичный ключ, добавьте к свойству nightId аннотацию @PrimaryKey . Установите параметр autoGenerate в true , чтобы Room генерировал идентификатор для каждой сущности. Это гарантирует уникальность идентификатора для каждой ночи.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. Добавьте аннотацию к оставшимся свойствам с помощью @ColumnInfo . Настройте имена свойств, используя параметры, как показано ниже.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. Скомпилируйте и запустите свой код, чтобы убедиться в отсутствии ошибок.

В этой задаче вы определите объект доступа к данным (DAO). В Android DAO предоставляет удобные методы для добавления, удаления и обновления базы данных.

При использовании базы данных Room вы обращаетесь к ней, определяя и вызывая функции Kotlin в коде. Эти функции Kotlin сопоставляются с SQL-запросами. Вы определяете эти сопоставления в DAO с помощью аннотаций, а Room создаёт необходимый код.

Представьте себе DAO как определение настраиваемого интерфейса для доступа к вашей базе данных.

Для распространённых операций с базой данных библиотека Room предоставляет удобные аннотации, такие как @Insert , @Delete и @Update . Для всего остального есть аннотация @Query . Вы можете написать любой запрос, поддерживаемый SQLite.

В качестве дополнительного бонуса, когда вы создаете запросы в Android Studio, компилятор проверяет ваши SQL-запросы на наличие синтаксических ошибок.

Для базы данных с данными о сне вам необходимо иметь возможность выполнять следующие действия:

  • Вставьте новые ночи.
  • Обновите существующую ночь, чтобы обновить время окончания и рейтинг качества.
  • Получите конкретную ночь в зависимости от ее тональности.
  • Получите все ночи , чтобы вы могли их отобразить.
  • Получите последнюю ночь.
  • Удалить все записи в базе данных.

Шаг 1: Создайте SleepDatabase DAO

  1. В пакете database откройте SleepDatabaseDao.kt .
  2. Обратите внимание, что interface SleepDatabaseDao аннотирован @Dao . Все объекты DAO должны быть аннотированы ключевым словом @Dao .
@Dao
interface SleepDatabaseDao {}
  1. Внутри тела интерфейса добавьте аннотацию @Insert . Под @Insert добавьте функцию insert() , которая принимает экземпляр класса Entity SleepNight в качестве своего аргумента.

    Вот и всё. Room сгенерирует весь необходимый код для добавления SleepNight в базу данных. При вызове insert() из кода Kotlin Room выполняет SQL-запрос для добавления сущности в базу данных. (Примечание: вы можете назвать эту функцию как угодно.)
@Insert
fun insert(night: SleepNight)
  1. Добавьте аннотацию @Update с функцией update() для одного SleepNight . Обновляемая сущность — это сущность с тем же ключом, что и переданная. Вы можете обновить некоторые или все остальные свойства сущности.
@Update
fun update(night: SleepNight)

Для оставшейся функциональности нет удобной аннотации, поэтому вам придется использовать аннотацию @Query и предоставлять запросы SQLite.

  1. Добавьте аннотацию @Query с функцией get() , которая принимает Long key Аргумент и возвращает SleepNight , допускающий значение NULL. Если параметр отсутствует, вы увидите ошибку.
@Query
fun get(key: Long): SleepNight?
  1. Запрос передается в виде строкового параметра аннотации. Добавьте параметр в @Query . Сделайте его String , представляющей собой запрос SQLite.
  • Выберите все столбцы из таблицы daily_sleep_quality_table
  • WHERE nightId соответствует key аргументу :.

    Обратите внимание на :key . Двоеточие в запросе используется для ссылки на аргументы функции.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. Добавьте ещё один @Query с функцией clear() и SQLite-запросом для DELETE всех данных из таблицы daily_sleep_quality_table . Этот запрос не удаляет саму таблицу.

    Аннотация @Delete удаляет один элемент, и вы могли бы использовать @Delete и предоставить список ночей для удаления. Недостаток заключается в том, что вам нужно получить или знать содержимое таблицы. Аннотация @Delete отлично подходит для удаления отдельных записей, но неэффективна для удаления всех записей из таблицы.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. Добавьте @Query с функцией getTonight() . Сделайте так, чтобы SleepNight , возвращаемый функцией getTonight() допускал значение NULL, чтобы функция могла обрабатывать случай, когда таблица пуста. (Таблица пуста в начале и после очистки данных.)

    Чтобы получить значение «tonight» из базы данных, напишите SQLite-запрос, который вернет первый элемент списка результатов, отсортированного по nightId в порядке убывания. Используйте LIMIT 1 чтобы вернуть только один элемент.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. Добавьте @Query с функцией getAllNights() :
  • Запрос SQLite должен вернуть все столбцы из daily_sleep_quality_table , упорядоченные по убыванию.
  • Метод getAllNights() возвращает список сущностей SleepNight в виде LiveData . Room обновляет эти LiveData , что означает, что вам нужно явно получить данные только один раз.
  • Возможно, вам потребуется импортировать LiveData из androidx.lifecycle.LiveData .
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. Хотя вы не увидите никаких видимых изменений, запустите приложение, чтобы убедиться в отсутствии ошибок.

В этой задаче вы создаете базу данных Room , которая использует Entity и DAO, созданные вами в предыдущей задаче.

Вам необходимо создать абстрактный класс-держатель базы данных с аннотацией @Database . Этот класс имеет один метод, который либо создаёт экземпляр базы данных, если она не существует, либо возвращает ссылку на существующую базу данных.

Получение базы данных Room немного сложнее, поэтому вот общий процесс, прежде чем вы начнете работать с кодом:

  • Создайте public abstract класс, extends RoomDatabase . Этот класс будет выполнять функцию хранилища базы данных. Класс является абстрактным, поскольку Room создаёт реализацию автоматически.
  • Добавьте к классу аннотацию @Database . В аргументах объявите сущности для базы данных и укажите номер версии.
  • Внутри объекта- companion определите абстрактный метод или свойство, возвращающее SleepDatabaseDao . Room сгенерирует тело автоматически.
  • Вам нужен только один экземпляр базы данных Room для всего приложения, поэтому сделайте RoomDatabase одиночным экземпляром.
  • Используйте конструктор баз данных Room для создания базы данных только в том случае, если она не существует. В противном случае верните существующую базу данных.

Шаг 1: Создание базы данных

  1. В пакете database откройте SleepDatabase.kt .
  2. В файле создайте abstract класс SleepDatabase , который расширяет RoomDatabase .

    Добавьте к классу аннотацию @Database .
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. Вы увидите ошибку из-за отсутствующих сущностей и параметров версии. Аннотация @Database требует нескольких аргументов, чтобы Room мог создать базу данных.
  • Укажите SleepNight как единственный элемент со списком entities .
  • Установите version 1 При каждом изменении схемы вам придется увеличивать номер версии.
  • Установите exportSchema на false , чтобы не сохранять резервные копии истории версий схемы.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. Базе данных необходимо знать о DAO. В теле класса объявите абстрактное значение, возвращающее SleepDatabaseDao . Можно иметь несколько DAO.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. Ниже определите companion объект. Этот сопутствующий объект позволяет клиентам получать доступ к методам создания или получения базы данных без создания экземпляра класса. Поскольку единственное назначение этого класса — предоставление базы данных, нет смысла создавать его экземпляр.
 companion object {}
  1. Внутри объекта companion объявите частную переменную INSTANCE для базы данных, допускающую значение NULL, и инициализируйте её значением null . Переменная INSTANCE сохранит ссылку на базу данных после её создания. Это поможет избежать многократного открытия подключений к базе данных, что требует больших затрат.

Добавьте аннотацию @Volatile к INSTANCE . Значение переменной volatile никогда не будет кэшироваться, а все операции записи и чтения будут выполняться в основную память и из неё. Это помогает гарантировать, что значение INSTANCE всегда актуально и одинаково для всех потоков выполнения. Это означает, что изменения, внесённые одним потоком в INSTANCE , немедленно видны всем остальным потокам, и вы не столкнётесь с ситуацией, когда, скажем, два потока обновляют одну и ту же сущность в кэше, что могло бы создать проблему.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. Ниже INSTANCE , всё ещё внутри объекта companion , определите метод getInstance() с параметром Context , который понадобится конструктору базы данных. Возвращает тип SleepDatabase . Вы увидите ошибку, поскольку getInstance() пока ничего не возвращает.
fun getInstance(context: Context): SleepDatabase {}
  1. Внутри getInstance() добавьте блок synchronized{} . Передайте this , чтобы получить доступ к контексту.

    Несколько потоков могут одновременно запрашивать экземпляр базы данных, что приводит к появлению двух баз данных вместо одной. Эта проблема вряд ли возникнет в этом примере приложения, но возможна в более сложном приложении. Обёртывание кода для synchronized базы данных означает, что только один поток выполнения одновременно может войти в этот блок кода, что гарантирует однократную инициализацию базы данных.
synchronized(this) {}
  1. Внутри блока synchronized скопируйте текущее значение INSTANCE в локальную переменную instance . Это позволит воспользоваться функцией Smart Cast , которая доступна только для локальных переменных.
var instance = INSTANCE
  1. Внутри synchronized блока return instance в конце synchronized блока. Игнорируйте ошибку несоответствия возвращаемого типа; после завершения вы больше никогда не вернёте значение null.
return instance
  1. Над оператором return добавьте оператор if , чтобы проверить, является ли instance null, то есть база данных еще не существует.
if (instance == null) {}
  1. Если instance равен null , используйте конструктор баз данных для получения базы данных. В теле оператора if вызовите Room.databaseBuilder и укажите переданный вами контекст, класс базы данных и имя базы данных sleep_history_database . Чтобы устранить ошибку, вам потребуется добавить стратегию миграции и build() в следующих шагах.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. Добавьте требуемую стратегию миграции в конструктор. Используйте .fallbackToDestructiveMigration() .

    Обычно вам потребуется предоставить объект миграции со стратегией миграции на случай изменения схемы. Объект миграции — это объект, который определяет, как все строки старой схемы преобразуются в строки новой схемы, чтобы данные не терялись. Миграция выходит за рамки данной практической работы. Простое решение — удалить и пересоздать базу данных, что означает потерю данных.
.fallbackToDestructiveMigration()
  1. Наконец, вызовите .build() .
.build()
  1. Назначьте INSTANCE = instance как последний шаг внутри оператора if .
INSTANCE = instance
  1. Ваш окончательный код должен выглядеть так:
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. Создайте и запустите свой код.

Теперь у вас есть все необходимые элементы для работы с базой данных Room . Этот код компилируется и запускается, но вы не можете точно сказать, работает ли он. Поэтому сейчас самое время добавить несколько простых тестов.

Шаг 2: Тестирование SleepDatabase

На этом этапе вы запускаете предоставленные тесты для проверки работоспособности вашей базы данных. Это помогает убедиться в её работоспособности перед началом разработки. Предоставленные тесты являются базовыми. Для производственного приложения вам потребуется протестировать все функции и запросы во всех DAO.

Стартовое приложение содержит папку androidTest . Эта папка androidTest содержит модульные тесты, использующие инструментарий Android. Это замысловатый способ сказать, что для тестов требуется фреймворк Android, поэтому их нужно запускать на физическом или виртуальном устройстве. Конечно, вы также можете создавать и запускать чистые модульные тесты, не использующие фреймворк Android.

  1. В Android Studio в папке androidTest откройте файл SleepDatabaseTest .
  2. Чтобы раскомментировать код, выделите весь закомментированный код и нажмите сочетание клавиш Cmd+/ или Control+/ .
  3. Взгляните на файл.

Вот краткий обзор тестового кода, поскольку это еще один фрагмент кода, который можно использовать повторно:

  • SleepDabaseTest — это тестовый класс .
  • Аннотация @RunWith идентифицирует исполнителя тестов — программу, которая настраивает и выполняет тесты.
  • Во время настройки выполняется функция с аннотацией @Before , которая создаёт SleepDatabase в памяти с объектом SleepDatabaseDao . «В памяти» означает, что эта база данных не сохраняется в файловой системе и будет удалена после выполнения тестов.
  • Кроме того, при построении базы данных в памяти код вызывает другой специфичный для теста метод — allowMainThreadQueries . По умолчанию при попытке выполнить запросы в основном потоке возникает ошибка. Этот метод позволяет запускать тесты в основном потоке, что следует делать только во время тестирования.
  • В тестовом методе, аннотированном @Test , вы создаёте, вставляете и извлекаете SleepNight и проверяете их идентичность. Если что-то пойдёт не так, выдаётся исключение. В реальном тесте у вас будет несколько @Test методы.
  • После завершения тестирования выполняется функция, аннотированная @After , для закрытия базы данных.
  1. Щелкните правой кнопкой мыши тестовый файл на панели «Проект» и выберите «Запустить «SleepDatabaseTest» .
  2. После выполнения тестов проверьте на панели SleepDatabaseTest , что все тесты пройдены.

Поскольку все тесты пройдены, теперь вы знаете несколько вещей:

  • База данных создается корректно.
  • Вы можете вставить SleepNight в базу данных.
  • Вы можете вернуть SleepNight .
  • SleepNight имеет правильное значение качества.

Проект Android Studio: TrackMySleepQualityRoomAndTesting

При тестировании базы данных необходимо проверить все методы, определённые в DAO. Для завершения тестирования добавьте и выполните тесты для проверки остальных методов DAO.

  • Определите таблицы как классы данных, аннотированные @Entity . Свойства, аннотированные @ColumnInfo , определите как столбцы в таблицах.
  • Определите объект доступа к данным (DAO) как интерфейс, аннотированный @Dao . DAO сопоставляет функции Kotlin с запросами к базе данных.
  • Используйте аннотации для определения функций @Insert , @Delete и @Update .
  • Используйте аннотацию @Query со строкой запроса SQLite в качестве параметра для любых других запросов.
  • Создайте абстрактный класс, имеющий функцию getInstance() , которая возвращает базу данных.
  • Используйте инструментированные тесты для проверки корректной работы вашей базы данных и DAO. Вы можете использовать предоставленные тесты в качестве шаблона.

Курс Udacity:

Документация для разработчиков Android:

Другая документация и статьи:

В этом разделе перечислены возможные домашние задания для студентов, работающих над этой лабораторной работой в рамках курса, проводимого преподавателем. Преподаватель должен выполнить следующие действия:

  • При необходимости задавайте домашнее задание.
  • Объясните учащимся, как следует сдавать домашние задания.
  • Оцените домашние задания.

Преподаватели могут использовать эти предложения так часто или редко, как пожелают, и могут свободно задавать любые другие домашние задания, которые они сочтут подходящими.

Если вы работаете с этой лабораторной работой самостоятельно, можете использовать эти домашние задания для проверки своих знаний.

Ответьте на эти вопросы

Вопрос 1

Как указать, что класс представляет собой сущность для хранения в базе данных Room ?

  • Сделайте так, чтобы класс расширял DatabaseEntity .
  • Добавьте к классу аннотацию @Entity .
  • Добавьте к классу аннотацию @Database .
  • Сделайте так, чтобы класс расширял RoomEntity , а также аннотируйте класс @Room .

Вопрос 2

DAO (объект доступа к данным) — это интерфейс, который Room использует для сопоставления функций Kotlin с запросами к базе данных.

Как указать, что интерфейс представляет собой DAO для базы данных Room ?

  • Сделайте так, чтобы интерфейс расширял RoomDAO .
  • Сделайте так, чтобы интерфейс расширял EntityDao , а затем реализуйте метод DaoConnection() .
  • Добавьте к интерфейсу аннотацию @Dao .
  • Добавьте аннотацию к интерфейсу с помощью @RoomConnection .

Вопрос 3

Какие из следующих утверждений верны для базы данных Room ? Выберите все подходящие варианты.

  • Таблицы для базы данных Room можно определить как аннотированные классы данных.
  • Если вы возвращаете LiveData из запроса, Room будет обновлять LiveData , если LiveData изменится.
  • Каждая база данных Room должна иметь один и только один DAO.
  • Чтобы идентифицировать класс как базу данных Room , сделайте его подклассом RoomDatabase и аннотируйте его @Database .

Вопрос 4

Какие из следующих аннотаций можно использовать в интерфейсе @Dao ? Выберите все подходящие варианты.

  • @Get
  • @Update
  • @Insert
  • @Query

Вопрос 5

Как проверить работоспособность вашей базы данных? Выберите все подходящие варианты.

  • Напишите инструментированные тесты.
  • Продолжайте писать и запускать приложение, пока оно не отобразит данные.
  • Замените вызовы методов в интерфейсе DAO вызовами эквивалентных методов в классе Entity .
  • Запустите функцию verifyDatabase() предоставленную библиотекой Room .

Перейти к следующему уроку: 6.2 Сопрограммы и комната

Ссылки на другие практические занятия по этому курсу см. на целевой странице практических занятий по основам Android Kotlin .

,

Эта практическая работа входит в курс «Основы Android Kotlin». Вы получите максимальную пользу от этого курса, если будете выполнять практические работы последовательно. Все практические работы курса перечислены на целевой странице практической работы «Основы Android Kotlin» .

Введение

В большинстве приложений есть данные, которые необходимо сохранять даже после того, как пользователь закрывает приложение. Например, приложение может хранить плейлист, список игровых предметов, записи расходов и доходов, каталог созвездий или данные о сне за определенный период времени. Обычно для хранения постоянных данных используется база данных.

Room — это библиотека для работы с базами данных, входящая в состав Android Jetpack . Room берёт на себя многие задачи по настройке и конфигурированию базы данных и позволяет вашему приложению взаимодействовать с ней, используя обычные вызовы функций. По сути, Room — это уровень абстракции, работающий поверх базы данных SQLite. Терминология Room и синтаксис запросов для более сложных запросов соответствуют модели SQLite.

На рисунке ниже показано, как база данных Room вписывается в общую архитектуру, рекомендуемую в этом курсе.

Что вам уже следует знать

Вам должно быть знакомо:

  • Создание базового пользовательского интерфейса (UI) для приложения Android
  • Использование действий, фрагментов и представлений.
  • Навигация между фрагментами и использование Safe Args (плагина Gradle) для передачи данных между фрагментами.
  • Модели представлений, фабрики моделей представлений, а также LiveData и их наблюдатели. Эти темы, связанные с компонентами архитектуры, рассматриваются в предыдущей практической работе этого курса.
  • Базовые знания баз данных SQL и языка SQLite. Для быстрого ознакомления или освежения знаний см. SQLite Primer.

Чему вы научитесь

  • Как создать и взаимодействовать с базой данных Room для сохранения данных.
  • Как создать класс данных, определяющий таблицу в базе данных.
  • Как использовать объект доступа к данным (DAO) для сопоставления функций Kotlin с SQL-запросами.
  • Как проверить работоспособность вашей базы данных.

Что ты будешь делать?

  • Создайте базу данных Room с интерфейсом для данных о ночном сне.
  • Протестируйте базу данных, используя предоставленные тесты.

В этой лабораторной работе вы создадите часть приложения, работающую с базой данных, для отслеживания качества сна. Приложение использует базу данных для хранения данных о сне с течением времени.

Приложение имеет два экрана, представленных фрагментами, как показано на рисунке ниже.

На первом экране, показанном слева, есть кнопки для запуска и остановки отслеживания. На экране отображаются все данные о сне пользователя. Кнопка «Очистить» безвозвратно удаляет все данные, собранные приложением для пользователя.

Второй экран, показанный справа, предназначен для выбора оценки качества сна. В приложении оценка представлена в числовом виде. В целях разработки приложение отображает как значки лиц, так и их числовые эквиваленты.

Поток действий пользователя выглядит следующим образом:

  • Пользователь открывает приложение и видит экран отслеживания сна.
  • Пользователь нажимает кнопку «Старт» . Время начала фиксируется и отображается на экране. Кнопка «Старт» отключается, а кнопка «Стоп» активируется.
  • Пользователь нажимает кнопку «Стоп» . Записывается время окончания и открывается экран качества сна.
  • Пользователь выбирает значок качества сна. Экран закрывается, и на экране отслеживания отображаются время окончания сна и качество сна. Кнопка «Стоп» отключается, а кнопка «Старт» активируется. Приложение готово к следующей ночи.
  • Кнопка «Очистить» активна, когда в базе данных есть данные. При нажатии кнопки «Очистить » все данные пользователя удаляются без возможности восстановления — сообщение «Вы уверены?» не появляется.

Это приложение использует упрощённую архитектуру, как показано ниже в контексте полной архитектуры. Приложение использует только следующие компоненты:

  • Контроллер пользовательского интерфейса
  • Просмотреть модель и LiveData
  • База данных A Room

Шаг 1: Загрузите и запустите стартовое приложение.

  1. Загрузите приложение TrackMySleepQuality-Starter с GitHub.
  2. Соберите и запустите приложение. Приложение отображает пользовательский интерфейс фрагмента SleepTrackerFragment , но не отображает данные. Кнопки не реагируют на нажатия.

Шаг 2: Проверьте стартовое приложение

  1. Взгляните на файлы Gradle:
  • Файл проекта Gradle
    В файле build.gradle уровня проекта обратите внимание на переменные, задающие версии библиотек. Версии, используемые в стартовом приложении, хорошо сочетаются друг с другом и хорошо работают с этим приложением. К моменту завершения этой лабораторной работы Android Studio может предложить вам обновить некоторые версии. Решение о том, обновлять или оставить версии, имеющиеся в приложении, зависит от вас. Если вы столкнётесь со «странными» ошибками компиляции, попробуйте использовать комбинацию версий библиотек, используемую в конечном решении .
  • Файл модуля Gradle. Обратите внимание на предоставленные зависимости для всех библиотек Android Jetpack, включая Room , а также зависимости для сопрограмм.
  1. Взгляните на пакеты и пользовательский интерфейс. Приложение структурировано по функциональным возможностям. Пакет содержит файлы-заглушки, в которые вы будете добавлять код в ходе этой серии практических занятий.
  • Пакет database для всего кода , относящегося к базе данных Room .
  • Пакеты sleepquality и sleeptracker содержат фрагмент, модель представления и фабрику моделей представления для каждого экрана.
  1. Взгляните на файл Util.kt , содержащий функции для отображения данных о качестве сна. Часть кода закомментирована, поскольку ссылается на модель представления, которую вы создадите позже.
  2. Взгляните на папку androidTest ( SleepDatabaseTest.kt ). Этот тест поможет вам проверить, работает ли база данных должным образом.

В Android данные представлены в классах данных, а доступ к ним и их изменение осуществляются с помощью вызовов функций. Однако в мире баз данных необходимы сущности и запросы .

  • Сущность представляет собой объект или понятие и его свойства, хранящиеся в базе данных. Класс сущности определяет таблицу, и каждый экземпляр этого класса представляет строку в таблице. Каждое свойство определяет столбец. В вашем приложении сущность будет хранить информацию о сне за ночь.
  • Запрос — это запрос данных или информации из таблицы базы данных или комбинации таблиц, а также запрос на выполнение действия с данными. Обычно запросы предназначены для получения, добавления и обновления сущностей. Например, можно запросить все зарегистрированные ночи сна, отсортированные по времени начала.

Room выполняет всю сложную работу по переходу от классов данных Kotlin к сущностям, которые можно хранить в таблицах SQLite, и от объявлений функций к запросам SQL.

Необходимо определить каждую сущность как аннотированный класс данных, а взаимодействия — как аннотированный интерфейс, объект доступа к данным (DAO) . Room использует эти аннотированные классы для создания таблиц в базе данных и запросов к ней.

Шаг 1: Создание объекта SleepNight

В этой задаче вы определяете одну ночь сна как аннотированный класс данных.

Для одной ночи сна вам необходимо записать время начала, время окончания и оценку качества.

И вам понадобится идентификатор, чтобы однозначно идентифицировать ночь.

  1. В пакете database найдите и откройте файл SleepNight.kt .
  2. Создайте класс данных SleepNight с параметрами для идентификатора, времени начала (в миллисекундах), времени окончания (в миллисекундах) и числовой оценки качества сна.
  • Вам необходимо инициализировать sleepQuality , установив его на -1 , что будет означать, что данные о качестве не были собраны.
  • Вам также необходимо инициализировать время окончания. Установите его равным времени начала, чтобы обозначить, что время окончания ещё не записано.
data class SleepNight(
       var nightId: Long = 0L,
       val startTimeMilli: Long = System.currentTimeMillis(),
       var endTimeMilli: Long = startTimeMilli,
       var sleepQuality: Int = -1
)
  1. Перед объявлением класса аннотируйте класс данных @Entity . Назовите таблицу daily_sleep_quality_table . Аргумент для tableName является необязательным, но рекомендуется. Вы можете найти другие аргументы в документации.

    Если будет предложено, импорт Entity и все другие аннотации из библиотеки androidx .
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
  1. Чтобы идентифицировать nightId в качестве основного ключа, аннотируйте nightId недвижимость @PrimaryKey . Установите параметр autoGenerate в true , чтобы Room генерировала идентификатор для каждого объекта. Это гарантирует, что идентификатор для каждой ночи уникален.
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
  1. Аннотировать оставшиеся свойства @ColumnInfo . Настройте имена свойств, используя параметры, как показано ниже.
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
       @PrimaryKey(autoGenerate = true)
       var nightId: Long = 0L,

       @ColumnInfo(name = "start_time_milli")
       val startTimeMilli: Long = System.currentTimeMillis(),

       @ColumnInfo(name = "end_time_milli")
       var endTimeMilli: Long = startTimeMilli,

       @ColumnInfo(name = "quality_rating")
       var sleepQuality: Int = -1
)
  1. Создайте и запустите свой код, чтобы убедиться, что у него нет ошибок.

В этой задаче вы определяете объект доступа данных (DAO). На Android DAO предоставляет удобные методы для вставки, удаления и обновления базы данных.

Когда вы используете базу данных Room , вы запросите базу данных, определяя и вызывая функции Kotlin в вашем коде. Эти функции Kotlin отображают запросы SQL. Вы определяете эти отображения в DAO, используя аннотации, а Room создает необходимый код.

Думайте о DAO как о определении пользовательского интерфейса для доступа к вашей базе данных.

Для общих операций базы данных библиотека Room предоставляет удобные аннотации, такие как @Insert , @Delete и @Update . Для всего остального есть аннотация @Query . Вы можете написать любой запрос, который поддерживается SQLite.

В качестве дополнительного бонуса, когда вы создаете свои запросы в Android Studio, компилятор проверяет ваши запросы SQL на наличие синтаксических ошибок.

Для базы данных для сна по ночам сна, вам нужно иметь возможность сделать следующее:

  • Вставьте новые ночи.
  • Обновите существующую ночь, чтобы обновить время окончания и качественный рейтинг.
  • Получите конкретную ночь на основе его ключа.
  • Получите все ночи , чтобы вы могли отобразить их.
  • Получите последнюю ночь.
  • Удалить все записи в базе данных.

Шаг 1: Создайте DAO SleepDatabase DAO

  1. В пакете database Open SleepDatabaseDao.kt .
  2. Обратите внимание, что interface SleepDatabaseDao аннотирован с @Dao . Все DAO должны быть аннотированы с ключевым словом @Dao .
@Dao
interface SleepDatabaseDao {}
  1. Внутри корпуса интерфейса добавьте аннотацию @Insert . Ниже @Insert добавьте функцию insert() , которая принимает экземпляр класса Entity Class SleepNight как его аргумент.

    Вот и все. Room будет генерировать весь необходимый код для вставки SleepNight в базу данных. Когда вы вызовите insert() из кода Kotlin, Room выполняет SQL -запрос, чтобы вставить объект в базу данных. (Примечание: вы можете вызвать функцию все, что хотите.)
@Insert
fun insert(night: SleepNight)
  1. Добавьте аннотацию @Update с функцией update() для одной SleepNight . Обновленная сущность - это сущность, которая имеет тот же ключ, что и тот, который передается. Вы можете обновить некоторые или все другие свойства объекта.
@Update
fun update(night: SleepNight)

Для оставшейся функциональности нет удобной аннотации, поэтому вы должны использовать аннотацию @Query и снабжение SQLite.

  1. Добавьте аннотацию @Query с функцией get() , которая занимает Long key Аргумент и возвращает нулевую SleepNight . Вы увидите ошибку для отсутствующего параметра.
@Query
fun get(key: Long): SleepNight?
  1. Запрос поставляется в виде струнного параметра для аннотации. Добавьте параметр в @Query . Сделайте это String , которая является запросом SQLite.
  • Выберите все столбцы в daily_sleep_quality_table
  • WHERE nightId соответствует: key аргумент.

    Обратите внимание :key . Вы используете нотацию толстой кишки в запросе для ссылки на аргументы в функции.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
  1. Добавьте еще один @Query с функцией clear() и запросом SQLite, чтобы DELETE все из daily_sleep_quality_table . Этот запрос не удаляет саму таблицу.

    Аннотация @Delete удаляет один элемент, и вы можете использовать @Delete и предоставить список ночей для удаления. Недостаток в том, что вам нужно получить или знать, что находится в таблице. Аннотация @Delete отлично подходит для удаления конкретных записей, но не эффективна для очистки всех записей из таблицы.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
  1. Добавьте @Query с функцией getTonight() . Сделайте SleepNight возвращаемую getTonight() , чтобы быть нулевой, чтобы функция могла справиться с случаем, когда таблица пуста. (Таблица пуста в начале, и после очистки данных.)

    Чтобы получить «сегодня вечером» из базы данных, напишите запрос SQLite, который возвращает первый элемент списка результатов, упорядоченных nightId в порядке убывания. Используйте LIMIT 1 , чтобы вернуть только один элемент.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
  1. Добавьте @Query с функцией getAllNights() :
  • Попросите запрос SQLite вернуть все столбцы из daily_sleep_quality_table , упорядоченного в порядке убывания.
  • Получите getAllNights() вернуть список сущностей SleepNight в качестве LiveData . Room держит эту LiveData обновленную для вас, что означает, что вам нужно явно получить данные только один раз.
  • Возможно, вам потребуется импортировать LiveData из androidx.lifecycle.LiveData .
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
  1. Хотя вы не увидите никаких видимых изменений, запустите свое приложение, чтобы убедиться, что у него нет ошибок.

В этой задаче вы создаете базу данных Room , которая использует Entity и DAO, которые вы создали в предыдущей задаче.

Вам нужно создать класс держателя абстрактного базы данных, аннотированный @Database . Этот класс имеет один метод, который либо создает экземпляр базы данных, если база данных не существует, либо возвращает ссылку на существующую базу данных.

Получение базы данных Room немного задействовано, поэтому вот общий процесс, прежде чем начать с кода:

  • Создайте public abstract класс, который extends RoomDatabase . Этот класс должен действовать как держатель базы данных. Класс абстрактный, потому что Room создает реализацию для вас.
  • Аннотируйте класс с @Database . В аргументах объявьте объекты для базы данных и установите номер версии.
  • Внутри companion объекта определите абстрактный метод или свойство, которое возвращает SleepDatabaseDao . Room будет генерировать для вас тело.
  • Вам нужен только один экземпляр базы данных Room для всего приложения, так что сделайте RoomDatabase синглтоном.
  • Используйте застройщик базы данных Room , чтобы создать базу данных, только если база данных не существует. В противном случае верните существующую базу данных.

Шаг 1: Создайте базу данных

  1. В пакете database Open SleepDatabase.kt .
  2. В файле создайте abstract класс под названием SleepDatabase , который расширяет RoomDatabase .

    Аннотируйте класс с @Database .
@Database()
abstract class SleepDatabase : RoomDatabase() {}
  1. Вы увидите ошибку для пропущенных объектов и параметров версии. Аннотация @Database требует нескольких аргументов, так что Room может построить базу данных.
  • Предоставьте ночью SleepNight как единственный предмет со списком entities .
  • Установите version как 1 Всякий раз, когда вы меняете схему, вам придется увеличить номер версии.
  • Установите exportSchema в false , чтобы не сохранить резервные копии истории версий схемы.
entities = [SleepNight::class], version = 1, exportSchema = false
  1. База данных должна знать о DAO. Внутри тела класса объявите абстрактную ценность, которая возвращает SleepDatabaseDao . Вы можете иметь несколько DAO.
abstract val sleepDatabaseDao: SleepDatabaseDao
  1. Ниже определить companion . Спутниковый объект позволяет клиентам получить доступ к методам создания или получения базы данных без создания подготовки к классу. Поскольку единственной целью этого класса является предоставление базы данных, нет никаких оснований для ее создания.
 companion object {}
  1. Внутри объекта companion объявите частный нулевый экземпляр INSTANCE для базы данных и инициализируйте его в null . Переменная INSTANCE будет соблюдать ссылку на базу данных, как только кто -то будет создан. Это помогает вам избежать многократного открытия соединений с базой данных, что дорого.

Аннотировать INSTANCE с @Volatile . Значение летучей переменной никогда не будет кэшировано, и все записи и чтения будут выполнены в основной памяти и обратно. Это помогает убедиться, что значение INSTANCE всегда актуально и одинаково для всех потоков выполнения. Это означает, что изменения, внесенные одним потоком в INSTANCE , немедленно видны всем другим потокам, и вы не получаете ситуации, когда, скажем, по два потока, каждый из которых обновляет одну и ту же сущность в кэше, что создало бы проблему.

@Volatile
private var INSTANCE: SleepDatabase? = null
  1. INSTANCE ниже, все еще внутри объекта companion , определите метод getInstance() с параметром Context , который потребуется застройщику базы данных. Верните тип SleepDatabase . Вы увидите ошибку, потому что getInstance() еще ничего не возвращает.
fun getInstance(context: Context): SleepDatabase {}
  1. Inside getInstance() , добавьте synchronized{} блок. Пропустите this , чтобы вы могли получить доступ к контексту.

    Несколько потоков могут потенциально запрашивать экземпляр базы данных одновременно, что приводит к двум базам данных вместо одной. Эта проблема вряд ли произойдет в этом приложении, но это возможно для более сложного приложения. Обертывание кода, чтобы получить базу данных в synchronized означает, что только один поток выполнения за раз может ввести этот блок кода, что гарантирует, что база данных инициализируется только один раз.
synchronized(this) {}
  1. Внутри синхронизированного блока скопируйте текущее значение INSTANCE в instance локальной переменной. Это может воспользоваться Smart Cast , который доступен только для локальных переменных.
var instance = INSTANCE
  1. Внутри synchronized блока return instance в конце synchronized блока. Игнорировать ошибку несоответствия типа возврата; Вы никогда не вернете NULL, как только закончите.
return instance
  1. Выше оператора return добавьте оператор if , чтобы проверить, является ли instance нулю, то есть, базы данных пока нет.
if (instance == null) {}
  1. Если instance null , используйте строитель базы данных, чтобы получить базу данных. В теле из оператора if вызовите Room.databaseBuilder и предоставьте контекст, в котором вы передали, класс базы данных и имя для базы данных, sleep_history_database . Чтобы удалить ошибку, вам придется добавить стратегию миграции и build() в следующих шагах.
instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database")
  1. Добавьте необходимую стратегию миграции в строитель. Использовать .fallbackToDestructiveMigration() .

    Обычно вам нужно предоставить миграционный объект с стратегией миграции, когда схема меняется. Миграционный объект - это объект, который определяет, как вы принимаете все строки со старой схемой и конвертируют их в строки в новой схеме, так что данные не теряются. Миграция выходит за рамки этого коделаба. Простое решение - уничтожить и восстановить базу данных, что означает, что данные теряются.
.fallbackToDestructiveMigration()
  1. Наконец, позвоните .build() .
.build()
  1. Назначить INSTANCE = instance как окончательный шаг внутри оператора if .
INSTANCE = instance
  1. Ваш окончательный код должен выглядеть так:
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}
  1. Создайте и запустите свой код.

Теперь у вас есть все строительные блоки для работы с базой данных вашей Room . Этот код компилирует и работает, но вы не можете сказать, действительно ли он работает. Итак, это хорошее время, чтобы добавить некоторые основные тесты.

Шаг 2: Проверьте SleepDatabase

На этом этапе вы запускаете предоставленные тесты, чтобы убедиться, что ваша база данных работает. Это помогает гарантировать, что база данных работает, прежде чем вы нарастаете на нее. Предоставленные тесты являются основными. Для производственного приложения вы будете выполнять все функции и запросы во всех DAO.

Приложение стартера содержит папку AndroidTest . Эта папка AndroidTest содержит модульные тесты, которые включают в себя приборы Android, что является причудливым способом сказать, что тесты нуждаются в платформе Android, поэтому вам необходимо запустить тесты на физическом или виртуальном устройстве. Конечно, вы также можете создавать и запустить чистые модульные тесты, которые не включают в себя платформу Android.

  1. В Android Studio, в папке AndroidTest , откройте файл SleepDataBasetest .
  2. Чтобы расстроить код, выберите все комментарии и нажмите сочетание Cmd+/ или Control+/ клавиатуры.
  3. Взгляните на файл.

Вот быстрый проход кода тестирования, потому что это еще один код, который вы можете повторно использовать:

  • SleepDabaseTest - это тестовый класс .
  • Аннотация @RunWith идентифицирует тестовый бегун, который является программой, которая устанавливает и выполняет тесты.
  • Во время настройки функция, аннотированная с @Before , выполняется, и создается в памяти SleepDatabase с SleepDatabaseDao . «В памяти» означает, что эта база данных не сохраняется в файловой системе и будет удалена после выполнения тестов.
  • Также при создании базы данных в памяти код вызывает другой метод, специфичный для теста, allowMainThreadQueries . По умолчанию вы получаете ошибку, если попытаетесь запустить запросы в главном потоке. Этот метод позволяет запускать тесты в основном потоке, что вы должны делать только во время тестирования.
  • В методе испытания, аннотированным с @Test , вы создаете, вставляете и получаете SleepNight , и утверждаете, что они одинаковы. Если что -то пойдет не так, бросьте исключение. В реальном тесте у вас будет несколько @Test методы
  • Когда тестирование выполняется, функция, аннотированная с помощью @After выполняется для закрытия базы данных.
  1. Щелкните правой кнопкой мыши тестовый файл на панели проекта и выберите «SleepDataBasetest» .
  2. После выполнения тестов убедитесь на панели SleepDataBasetest , что прошли все тесты.

Поскольку все тесты прошли, теперь вы знаете несколько вещей:

  • База данных создается правильно.
  • Вы можете вставить SleepNight в базу данных.
  • Вы можете вернуться в SleepNight .
  • SleepNight имеет правильное значение для качества.

Android Studio Project: TrackmysleepQuality и

При тестировании базы данных вам необходимо использовать все методы, определенные в DAO. Чтобы завершить тестирование , добавить и выполнить тесты, чтобы использовать другие методы DAO.

  • Определите свои таблицы как классы данных, аннотированные с помощью @Entity . Определите свойства, аннотированные с @ColumnInfo как столбцы в таблицах.
  • Определите объект доступа данных (DAO) как интерфейс, аннотированный с @Dao . DAO Карты Kotlin функционируют на запросы базы данных.
  • Используйте аннотации, чтобы определить функции @Insert , @Delete и @Update .
  • Используйте аннотацию @Query со строкой запроса SQLite в качестве параметра для любых других запросов.
  • Создайте абстрактный класс, который имеет функцию getInstance() , которая возвращает базу данных.
  • Используйте инструментальные тесты, чтобы проверить, что ваша база данных и DAO работают, как и ожидалось. Вы можете использовать предоставленные тесты в качестве шаблона.

Курс Udacity:

Документация разработчика Android:

Другая документация и статьи:

В этом разделе перечислены возможные домашние задания для студентов, которые работают через этот Codelab в рамках курса, во главе с инструктором. Инструктор должен сделать следующее:

  • Назначьте домашнее задание, если это необходимо.
  • Сообщите студентам, как отправлять домашние задания.
  • Оценка домашних заданий.

Преподаватели могут использовать эти предложения столько, сколько они хотят, и должны свободно назначать любую другую домашнюю работу, которая, по их мнению, является подходящей.

Если вы работаете над этим CodeLab самостоятельно, не стесняйтесь использовать эти домашние задания, чтобы проверить свои знания.

Ответьте на эти вопросы

Вопрос 1

Как вы указываете, что класс представляет собой объект для хранения в базе данных Room ?

  • Сделайте класс расширение DatabaseEntity .
  • Анотировать класс @Entity .
  • Аннотируйте класс с @Database .
  • Сделайте класс расширить RoomEntity , а также аннотировать класс с помощью @Room .

Вопрос 2

DAO (объект доступа к данным) - это интерфейс, который Room использует для картирования функций Kotlin с запросами базы данных.

Как вы указываете, что интерфейс представляет собой DAO для базы данных Room ?

  • Сделайте интерфейс расширить RoomDAO .
  • Сделайте интерфейс Extend EntityDao , затем реализуйте метод DaoConnection() .
  • Аннотируйте интерфейс с @Dao .
  • Аннотируйте интерфейс с @RoomConnection .

Вопрос 3

Какие из следующих утверждений верны в базу данных Room ? Выберите все, что применимо.

  • Вы можете определить таблицы для базы данных Room как аннотированные классы данных.
  • Если вы вернете LiveData из запроса, Room будет держать LiveData в обновленной для вас, если изменится LiveData .
  • Каждая база данных Room должна иметь один, и только один, дао.
  • Чтобы идентифицировать класс в качестве базы данных Room , сделайте его подклассом RoomDatabase и аннотируйте его @Database .

Вопрос 4

Какие из следующих аннотаций вы можете использовать в своем интерфейсе @Dao ? Выберите все, что применимо.

  • @Get
  • @Update
  • @Insert
  • @Query

Вопрос 5

Как вы можете проверить, что ваша база данных работает? Выберите все, что применимо.

  • Напишите инструментальные тесты.
  • Продолжайте писать и запустить приложение, пока оно не отобразит данные.
  • Замените вызовы на методы в интерфейсе DAO на вызовы эквивалентных методов в классе Entity .
  • Запустите функцию verifyDatabase() предоставленную библиотекой Room .

Начните на следующий урок: 6.2 Coroutines и комната

Ссылки на другие коделабы в этом курсе см. Взъективную целевую страницу CodeLabs Foundals Android Kotlin .