Использование комнаты в Jetpack Compose

Статья здесь: https://flatteredwithflutter.com/using-room-in-jetpack-compose/

Вступление

Уровень: начальный

Мы кратко рассмотрим:

  1. Использование комнаты в Jetpack Compose
  2. Написание CRUD-операций
  3. Написать тест для базы данных

Примечание. В этой статье предполагается, что читатель знает о Jetpack Compose.

Использование комнаты в Jetpack Compose

Что такое комната?

Согласно документации,

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

Настроить комнату в Compose

Чтобы использовать Room в своем приложении, добавьте следующие зависимости в build.gradle файл приложения:

dependencies {
  def room_version = "2.2.6"

  // FOR ROOM
  implementation "androidx.room:room-runtime:$room_version"
  kapt "androidx.room:room-compiler:$room_version"
  
  // optional - Kotlin Extensions and Coroutines support for Room
  implementation "androidx.room:room-ktx:$room_version"
  // END FOR ROOM
}

Компоненты комнаты

Комната состоит из 3 основных компонентов:

Внутри нашего кода это выглядит так

Чтобы использовать состояние в составе реактивного ранца, обратитесь к этому



Написание CRUD-операций

Давайте посмотрим, что будет в каждом файле в базе данных (на скриншоте выше).

  1. TodoItem (наш класс сущности)

Мы создаем объект под названием TodoItem

  • Класс аннотируется @Entity и именем таблицы.
@Entity(tableName = "my_todo_list")
data class TodoItem(
    @PrimaryKey(autoGenerate = true)
    var itemId: Long = 0L,
    @ColumnInfo(name = "item_name")
    val itemName: String,
    @ColumnInfo(name = "is_completed")
    var isDone: Boolean = false
)

2. TodoDatabaseDao (наши объекты доступа к данным)

Создаем интерфейс (TodoDatabaseDao)

  • Класс помечен @ Dao
@Dao
interface TodoDatabaseDao {
    @Query("SELECT * from my_todo_list")
    fun getAll(): LiveData<List<TodoItem>>
    @Query("SELECT * from my_todo_list where itemId = :id")
    fun getById(id: Int) : TodoItem?
    @Insert
    suspend fun insert(item:TodoItem)
    @Update
    suspend fun update(item:TodoItem)
    @Delete
    suspend fun delete(item:TodoItem)
    @Query("DELETE FROM my_todo_list")
    suspend fun deleteAllTodos()
}
  • Аннотация запроса используется для написания пользовательских запросов (например, прочитать или удалить все в нашем случае)
  • Аннотации Insert, Update, Delete выполняют операции CUD.
  • Мы отмечаем эти функции как приостановленные (чтобы мы могли вызывать их внутри сопрограмм).

3. TodoDatabase (наш класс базы данных)

Мы определяем абстрактный класс (TodoDatabase), который расширяет RoomDatabase

  • Этот класс помечен @ Database
@Database(entities = [TodoItem::class], version = 1)
abstract class TodoDatabase : RoomDatabase() {
    abstract fun todoDao(): TodoDatabaseDao
    companion object {
         private var INSTANCE: TodoDatabase? = null
         fun getInstance(context: Context): TodoDatabase {
            synchronized(this) {
                var instance = INSTANCE
                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        TodoDatabase::class.java,
                        "todo_list_database"
                    ).fallbackToDestructiveMigration()
                     .build()
                 INSTANCE = instance
               }
             return instance
           }
       }
   }
}
  • Поток, который входит в синхронизированный метод, получает блокировку, и никакой другой поток не может войти в метод, пока блокировка не будет снята. Kotlin предлагает те же функции, что и аннотация Synchronized.

4. TodoRepository (наш класс репозитория)

Мы создаем класс репозитория (TodoRepository), который принимает TodoDatabaseDao в качестве параметра конструктора.

class TodoRepository(private val todoDatabaseDao: TodoDatabaseDao) {
    val readAllData : LiveData<List<TodoItem>> =  todoDatabaseDao.getAll()
    suspend fun addTodo(todoItem: TodoItem) {
        todoDatabaseDao.insert(todoItem)
    }
    suspend fun updateTodo(todoItem: TodoItem) {
        todoDatabaseDao.update(todoItem)
    }
    suspend fun deleteTodo(todoItem: TodoItem) {
        todoDatabaseDao.delete(todoItem)
    }
    suspend fun deleteAllTodos() {
        todoDatabaseDao.deleteAllTodos()
    }
}
  • Все взаимодействия с базой данных выполняются через этот уровень репозитория.
  • Поскольку наши реализации были функциями приостановки, то же самое и в наших определениях.
  • Мы предоставляем readAllData типа LiveData, поэтому мы можем наблюдать за изменениями и уведомлять пользовательский интерфейс.

Написать тест для базы данных

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

  • Создайте тестовый класс TodoDatabaseTest в папке androidTest.
  • Мы аннотируем наш класс с помощью AndroidJUnit4Это то, что будет управлять тестами для одного класса.
@RunWith(AndroidJUnit4::class)
class TodoDatabaseTest {

    private lateinit var todoDao: TodoDatabaseDao
    private lateinit var db: TodoDatabase

    @Before
    fun createDb() {
        val context =               InstrumentationRegistry.getInstrumentation().targetContext

        db = Room.inMemoryDatabaseBuilder(context, TodoDatabase::class.java)
            .allowMainThreadQueries()
            .build()

        todoDao = db.todoDao()
    }

    @After
    @Throws(IOException::class)
    fun deleteDb() {
        db.close()
    }

    @Test
    @Throws(Exception::class)
    fun insertAndGetTodo() = runBlocking {
        val todoItem = TodoItem(itemId = 1, itemName = "Dummy Item", isDone = false)
        todoDao.insert(todoItem)
        val oneItem = todoDao.getById(1)
        assertEquals(oneItem?.itemId, 1)
    }
}
  • Перед запуском теста мы создаем базу данных с помощью createDb.
  • После запуска теста мы удаляем базу данных с помощью deleteDb
  • Во время выполнения теста мы вызываем insertAndGetTodo
  • Поскольку это всего лишь тест, поэтому мы используем runBlocking.
  • Создайте задачу и утвердите ее в соответствии с созданной ценностью.

  • Нажмите Запустить TodoDatabaseTest, чтобы запустить тест.
  • Если все в порядке, вы должны увидеть

Source code

Интересующие статьи: