fbpx

Создание локальной БД для хранения информации о тренировках

Для хранения информации о датах тренировок нам нужно будет создать локальное хранилище. Для этого мы воспользуемся Room. Если вы никогда не работали с Room, то более подробно можете прочитать в этом мини-курсе  Там описаны базовые понятия и по шагам реализовано взаимодействие в БД

Для того, чтобы использовать Room необходимо

  1. Добавить нужные зависимости
  2. Определить Entity, Dao и саму базу данных
  3. Реализовать нужные методы в Dao

Добавляем нужные библиотеки

В блок dependencies в файле build.gradle вставьте следующие зависимости. Чтобы не дублировать константы версий одной и тоже библиотеки рекомендуется выносить их в отдельный блок,  хранящий версии библиотек. И потом ссылаться на них. Пример $rootProject.roomVersion

Более подробно прочитать, как хранить в порядке список библиотек в gradle-файлах вы можете прочитать в статье Наводим порядок в build.gradle

// Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
implementation "androidx.room:room-ktx:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"

Определяем Entity

Для хранения данных в БД нам нужна таблица – или Entity в терминах Room. В качестве Entity мы будем использовать класс ReminderData, который будет хранить информацию о тренировках и датах напоминаний.

@Entity(tableName = "reminder_data")
@Parcelize
data class ReminderData(
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0,
    var name: String? = null,
    var type: WorkoutType = WorkoutType.Swimming,
    var hour: Int = 0,
    var minute: Int = 0,
    var days: List<String?>? = null
) : Parcelable

Я не буду повторять, что значит каждая из аннотаций – это всё отлично расписано и можете прочитать в этом мини-курсе

Создание DAO

Для управления записями таблицы, необходимо создать Data Access Object

@Dao
interface ReminderDao {

    @Query("SELECT * from reminder_data")
    fun getReminderData(): List<ReminderData>

    @Query("SELECT * from reminder_data where id= :id")
    fun getReminderById(id: Long): ReminderData?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(reminder: ReminderData): Long

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(reminders: List<ReminderData>)

    @Update(onConflict = OnConflictStrategy.REPLACE)
    fun update(reminder: ReminderData)

    @Query("DELETE FROM reminder_data")
    fun deleteAll()

    @Query("DELETE FROM reminder_data where id LIKE :id")
    fun deleteById(id: Long)
}

Создание Базы Данных

@Database(entities = [ReminderData::class], version = 1)
@TypeConverters(WorkoutTypeConverter::class, DaysConverter::class)
abstract class WorkoutDatabase : RoomDatabase() {

    abstract fun reminderDataDao(): ReminderDao
}

Здесь указаны так называемые конверторы – они необходимы для сохранения объектов у которых сложный тип данных.

Создание конверторов данных

Для сохранения типов тренировки (Бег/Плавание/Езда на велосипеде) нужно написать соответствующий конвертор данных

class WorkoutTypeConverter {
    @TypeConverter
    fun toStatus(numeral: Int): WorkoutType {
        return when (numeral) {
            0 -> WorkoutType.Swimming
            1 -> WorkoutType.Cycling
            2 -> WorkoutType.Running
            else -> WorkoutType.Swimming
        }
    }

    @TypeConverter
    fun fromStatus(type: WorkoutType): Int {
        return when (type) {
            WorkoutType.Swimming -> 0
            WorkoutType.Cycling -> 1
            WorkoutType.Running -> 2
        }
    }
}

Кроме этого, нам необходимо сохранять список дней, по которым будет проходить тренировка – соответственно необходим конвертор из спика строк в строку – это самый простой способ сохранить список. В противном случае необходимо создать ещё одну таблицу в которой будут храниться дни и реализовать отношение 1 ко многим или многие ко многим. Для простоты сохраним данные в строку

const val SEPARATOR = ";"

class DaysConverter {
    @TypeConverter
    fun toDaysString(days: List<String>): String {
        return days.joinToString(SEPARATOR)
    }

    @TypeConverter
    fun fromDaysString(days: String): List<String> {
        return days.split(SEPARATOR).toList()
    }
}

Инициализация Базы Данных

Для того, чтобы создать БД и использовать для сохранения/чтения данных добавьте следующий код в класс FitnessApp

class FitnessApp : Application() {

    lateinit var app: FitnessApp
    private lateinit var database: WorkoutDatabase

    override fun onCreate() {
        super.onCreate()

        app = this
        database = Room.databaseBuilder(this, WorkoutDatabase::class.java, "database")
            // Только для примера, в реальном проекте так не стоит делать
            .allowMainThreadQueries()
            .build()
            .....
    }

    fun getInstance(): FitnessApp {
        return app
    }

    fun getDatabase(): WorkoutDatabase {
        return database
    }
}

Метод  .allowMainThreadQueries() – позволяет обращаться к БД в главном потоке, в реальном приложении этого стоит избегать – так как операции чтения из БД могут быть долгими и вы получите ANR

Сохранение ReminderData

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

private fun createReminder(name: String, dateType: WorkoutType, days: List<String?>?): Long {
      reminderData.name = name
      reminderData.type = dateType
      reminderData.days = days

      return ReminderLocalRepostiory(activity?.applicationContext).saveReminder(reminderData)
  }

Для чтения и сохранения данных из БД давайте создадим репозиторий ReminderLocalRepostiory

Метод saveReminder() определен в репозитории

class ReminderLocalRepostiory(val context: Context?) {

    private val roomDatabase = (context?.applicationContext as FitnessApp).getDatabase()
    private val dao = roomDatabase.reminderDataDao()

    fun getReminders(): List<ReminderData> {
        return dao.getReminderData()
    }

    fun saveReminder(reminderData:ReminderData):Long{
        return dao.insert(reminderData)
    }
}

Чтение ReminderData из БД

Для чтения данных из БД давайте создадим метод getReminders()

class ReminderLocalRepostiory(val context: Context?) {

    private val roomDatabase = (context?.applicationContext as FitnessApp).getDatabase()
    private val dao = roomDatabase.reminderDataDao()

    fun getReminders(): List<ReminderData> {
        return dao.getReminderData()
    }
}

Чтение данных в FirstFragment теперь будет выглядеть так

mainAdapter = MainAdapter(this, ReminderLocalRepostiory(activity?.applicationContext).getReminders())

Отлично! Теперь наше приложение умеет сохранять информацию о дате и времени тренировок и отображать её пользователю. В следующих уроках эта информация понадобится для того, чтобы запланировать уведомление и напомнить пользователю о тренировке.

Результат этого урока вы можете найти по ссылке в ветке feature/04-create-database