Асинхронно перебирать все записи комнаты только один раз - PullRequest
0 голосов
/ 08 апреля 2020

Я хотел бы выполнить асинхронную операцию для каждой записи в большой таблице Room.

Я подумал, что мог бы добавить метод, возвращающий Flow в моем DAO следующим образом:

    @Query("SELECT * FROM events")
    fun getEvents(): Flow<EventEntity>

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

Моя цель - перебрать все сущности только один раз . Я не хочу поведения "наблюдаемости". Кроме того, поскольку таблица очень большая, я не хочу загружать все записи сразу в список, чтобы не занимать слишком много памяти.

Не могли бы вы порекомендовать какое-нибудь решение, пожалуйста?

1 Ответ

0 голосов
/ 08 апреля 2020

Создайте новый метод, который не использует Flow.

@Query("SELECT id FROM events")
fun getAllIds(): List<Int> // If your primary key is Integer.

@Query("SELECT * FROM events WHERE id = :id")
fun getById(id: Int): EventEntity?

Используйте Kotlin сопрограммы для вызова этого метода в потоке ввода-вывода.

Может быть несколько стратегий для загрузки одного грести за раз. Это самое простое - получить все идентификаторы и загрузить каждый элемент по одному.

suspend fun getEvents() {
    withContext(Dispatchers.IO) {
        // Get entities from database on IO thread.
        val ids = dao.getAllIds()
        ids.forEach { id ->
             val event = dao.getById(id)
        }
    }
}

Подход на основе нумерации страниц

Этот подход предполагает, что у вас есть столбец, в котором хранится метка времени (например, created_at).

@Query("SELECT * from events WHERE created_at > :timestamp ORDER BY created_at LIMIT 10")
fun getAfter(timestamp: Long): List<EventEntity>

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

suspend fun getEvents() {
    withContext(Dispatchers.IO) {
        var timestamp: Long = 0
        while (true) {
          // Get entities from database on IO thread.
          val events = dao.getAfter(timestamp)

          // Process this batch of events

          // Get timestamp for pagination offset.
          timestamp = events.maxBy { it.createAt }?.createAt ?: -1

          if (timestamp == -1) {
             // break the loop. This will be -1 only if the list of events are empty.
          }

        }
    }
}
...