Я читал официальное приложение архитектуры Android, чтобы узнать больше о шаблоне архитектуры, и я вижу, что мы возвращаем Flow, возвращая SomeOtherClass из методов, определенных в репозитории.
package com.example.android.architecture.blueprints.todoapp.data
import kotlinx.coroutines.flow.Flow
/**
* Interface to the data layer.
*/
interface TaskRepository {
fun getTasksStream(): Flow<List<Task>>
suspend fun getTasks(forceUpdate: Boolean = false): List<Task>
suspend fun refresh()
fun getTaskStream(taskId: String): Flow<Task?>
suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Task?
suspend fun refreshTask(taskId: String)
suspend fun createTask(title: String, description: String): String
suspend fun updateTask(taskId: String, title: String, description: String)
suspend fun completeTask(taskId: String)
suspend fun activateTask(taskId: String)
suspend fun clearCompletedTasks()
suspend fun deleteAllTasks()
suspend fun deleteTask(taskId: String)
}
При создании приложения, как лучше решить, когда нам нужно вернуть поток, а не реальный объект из репозитория? Я также вижу, что потоки не являются функциями приостановки, а методы, которые возвращают объект, являются функциями приостановки. Почему так?
Я пишу приведенное ниже объяснение с предположением, что это репозиторий, поддерживаемый базой данных, но те же концепции применяются, если репозиторий резервируется другими источниками, такими как веб-сервер.
Потоки холодны — сам объект только определяет, что он будет делать при сборе, но на самом деле еще не делает этого. Для создания облегченного объекта Flow не требуется чтения базы данных, поэтому нет причин приостанавливать функцию, если она возвращает поток.
Поток, возвращаемый Room, отслеживает базу данных. Когда вы начинаете его собирать, он сначала выдает текущее значение связанного запроса к базе данных. Затем он снова генерируется каждый раз при изменении базы данных, потому что результат запроса может быть другим после модификации.
Функция приостановки для извлечения чего-либо из базы данных просто возвращает результат выполнения запроса в момент его вызова. Вы также можете определить функции приостановки в DAO для внесения изменений в базу данных (например, вставки строк, удаления строк или редактирования строк). Для них не имеет смысла возвращать потоки. Потоки возвращают данные, поэтому они относятся только к запросам.
Когда вы определяете функцию для извлечения чего-либо, выбор между функцией потока и функцией приостановки сводится к тому, как вы будете использовать ее на уровне пользовательского интерфейса.
Другой вариант — просто всегда возвращать поток и позволять вызывающим сторонам решать, будут ли они единственными получать текущее значение с помощью функции приостановки, что можно сделать, вызвав .first()
в потоке.
Означает ли это, что все остальные сетевые вызовы API никогда не должны возвращать поток?
Что ж, вы можете вернуть поток, который время от времени повторяет запрос для получения обновленных данных.
Просто для полной ясности того, как на самом деле реализовать это, означает ли это, что у нас всегда будет сопрограмма, работающая в диспетчере ввода-вывода вместе с операторами задержки (скажем, 20 секунд), и которая выдает ответ, который мы получили от запроса сервера, и мы используем этот поток в слое пользовательского интерфейса? Знаете ли вы, как база данных Room обрабатывает обновление таблиц в реальном времени?
В комнате не используется цикл с таймером. Каждый раз, когда вы вызываете функцию, которая изменяет базу данных, она уведомляет все потоки (или LiveData или RxObservables), что она вернула и в настоящее время собирает, чтобы они знали, что нужно сделать новый запрос и выдать результат. Это, конечно, гораздо более желательно, чем многократное упреждающее сканирование базы данных. Но чтобы сделать таймер, вам понадобится диспетчер ввода-вывода только при вызове блокирующих функций. Если у вас есть приостанавливающий запрос на получение REST, вам не нужно указывать диспетчер. Вы можете определить свой поток как....
fun foo() = flow { while (true) { emit(getSomethingViaSuspendFunction()); delay(20_000L) } }
В этом вопросе, на который я ответил вчера, есть некоторая связанная информация. stackoverflow.com/questions/76030366/…