#android #viewmodel #coroutinescope
Вопрос:
Привет, у меня есть вопрос: как лучше всего передать viewModelScope классу помощника/менеджера, используемому в ViewModel.
Например, я хотел бы создать класс AccountManager, который будет заботиться об учетной записи, выполнять некоторые асинхронные операции с задачами, чтение/запись из базы данных и т.д… Я хочу предоставить результат через поток Kotlin и собрать его в моей модели представления, и по этой причине мне нужно получить доступ к области видимости (чтобы я мог выдавать значения), потому что создание новой области не кажется очень хорошей идеей.
Я использую инъекцию зависимостей Hilt, и я хотел бы ввести менеджера учетных записей в качестве зависимости в ViewModel. Кроме того, стоит отметить одну вещь: я хотел бы использовать этот менеджер учетных записей более чем в одном месте в своем приложении, потому что он инкапсулирует некоторые повторяющиеся операции. Существует опасение, что если я создам AccountManager как одноэлементный, я могу создать утечку памяти, если передам ему определенную область просмотра… Итак, в принципе, мне нужно ваше мнение, какое было бы лучшее решение для этого?
Ответ №1:
Для начала вам нужно понять, что emit
и collect
называются одинаково CoroutineScope
. Пожалуйста, посмотрите исходный код. Нет необходимости явно переносить область действия куда-либо
public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
collect(object : FlowCollector<T> {
override suspend fun emit(value: T) = action(value)
})
collect
Метод таков suspend
. Внутренне он запускает как блок потока, так и блок сбора. Поэтому вся работа по отправке и приему данных будет выполняться в сопрограмме, в которой мы вызываем collect()
метод.
class TestViewModel(private val manager: Manager) : ViewModel() {
private fun load() =
viewModelScope.launch(IO) {
manager.doWork1().collect {
}
}
private fun load2() =
viewModelScope.launch {
manager.doWork2().collect {
}
}
}
Класс менеджера
class Manager {
fun doWork1() = flow {
for (k in 1..10) {
val t = newValue(k)
emit(t)
}
}
suspend fun doWork2() = flow {
withContext(IO) {
for (k in 1..10) {
val t = newValue(k)
emit(t)
}
}
}
private suspend fun newValue(param: Int) {
delay(5000)
Random(param).nextInt()
}
}
Комментарии:
1. Мой менеджер использует api, который имеет свои собственные внутренние обратные вызовы. Поэтому я хотел бы использовать сопрограммный канал и сделать что-то вроде:
val flow = channel.receiveAsFlow()
И сопрограммный канал сможет принимать параметр запечатанного класса и отправлять его.channel.send(ManagerClassEvent())
Теперь, чтобы отправить событие с каналом, мне нужна область сопрограммы. Я пробовал использовать LiveData, а затем преобразовать его в поток, но он использует только объединенный поток, поэтому может случиться так, что я потеряю некоторые данные… Насколько хорошим/плохим решением было бы добавить метод в менеджер, который передавал бы ему область действия:fun setScope(scope : CoroutineScope)
?2. Я думаю, вы можете просто пометить свои методы как приостановленные , где вам нужно вызвать channel.send(ManagerClassEvent ()) , не нужно делиться сопрограммой , когда