Выдавать сопрограмму Flow from Room при обратной заправке через сетевой запрос - PullRequest
1 голос
/ 15 октября 2019

Моя архитектура выглядит следующим образом:

  1. Методы Дао, возвращающие Flow<T>:

    @Query("SELECT * FROM table WHERE id = :id")
    fun itemById(id: Int): Flow<Item>
    
  2. Уровень репозитория, возвращающий элементы из БДно также обратная засыпка из сети:

    (* Нужна помощь здесь - это не работает, как задумано **)

    fun items(): Flow<Item> = flow {
        // Immediately emit values from DB
        emitAll(itemDao.itemById(1))
    
        // Backfill DB via network request without blocking coroutine
        itemApi.makeRequest()
            .also { insert(it) }
    }
    
  3. Слой ViewModel принимает поток, применяялюбые преобразования и преобразование их в LiveData с использованием .asLiveData ():

    fun observeItem(): LiveData<Item> = itemRepository.getItemFlow()
        .map { // apply transformation to view model }
        .asLiveData()
    
  4. Фрагмент, наблюдающий выбросы LiveData и обновляемый пользовательский интерфейс:

    viewModel.item().observeNotNull(viewLifecycleOwner) {
        renderUI(it)
    }
    

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

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

Есть мысли?

РЕДАКТИРОВАТЬ

Решение Марко хорошо работает для меня, но я попробовал подобный подход, например так:

suspend fun items(): Flow<List<Cryptocurrency>> = coroutineScope {
    launch {
        itemApi.makeRequest().also { insert(it) }
    }

    itemDao.itemById(1)
}

1 Ответ

1 голос
/ 15 октября 2019

Похоже, вы описываете фоновую задачу, которую хотите запустить. Для этого вам нужен доступ к вашей области сопрограммы, поэтому items() должна быть функцией расширения для CoroutineScope:

fun CoroutineScope.items(): Flow<Item> {
    launch {
        itemApi.makeRequest().also { insert(it) }
    }
    return flow {
        emitAll(itemDao.itemById(1))
    }
}

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

fun items(): Flow<Item> = flow {
    coroutineScope {
        val lateItem = async { itemApi.makeRequest().also { insert(it) } }
        emitAll(itemDao.itemById(1))
        emit(lateItem.await())
    }
}
...