Получить область просмотра в вспомогательном классе, используемом в ViewModel

#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 ()) , не нужно делиться сопрограммой , когда