Учитывая следующие компоненты
data class Account(val name: String)
data class GetAccountRequest(val name: String)
@Dao
interface AccountDao {
@Query("SELECT * FROM accounts ORDER BY name ASC")
fun all(): LiveData<List<Account>>
}
interface AccountOperations {
@GET("/foo/account")
suspend fun getAccount(@Body request: GetAccountRequest): Account
}
class AccountRepository(private val dao: AccountDao, private val api: AccountOperations) {
val accounts: LiveData<List<Account>> = dao.all()
suspend fun refresh(name: String) {
val account = api.getAccount(GetAccountRequest(name))
dao.insert(account)
}
}
Я работаю над приложением Android, которое использует эти компоненты (питание от Room для базы данных и Retrofit для доступа к API).
В моемViewModel Я поддерживаю RecyclerView, который перечисляет все учетные записи. Я даю пользователям возможность обновить этот список вручную. Соответствующая (часть) ViewModel выглядит следующим образом:
fun refresh() {
viewModelScope.launch {
repository.accounts.value?.forEach {
launch { repository.refresh(it.name) }
}
}
Timber.i("Done refreshing!")
}
Я хочу, чтобы обновление обновляло все учетные записи параллельно, поэтому я использую launch
. Я также решил сделать это в ViewModel, а не в хранилище, поскольку для этого потребовалось бы запустить новую сопрограмму в хранилище. Что за этот пост не рекомендуется, так как репозитории не имеют естественного жизненного цикла.
Вышеуказанная функция refresh
вызывается из пользовательского интерфейса и показывает индикатор обновления, пока RecyclerViewобновляется. Поэтому я хочу остановить этот индикатор, как только все учетные записи будут обновлены.
Мой код, как показано выше, этого не делает, так как он запустит все обновления и затем напечатает инструкцию журнала до того, как все обновления будут завершены. ,В результате индикатор обновления исчезает, хотя обновления все еще существуют.
Поэтому мой вопрос (наконец-то) заключается в следующем: как мне выполнить рефакторинг кода, чтобы он выполнял все обновления параллельно, но при этом refresh
не делаетне вернетесь, пока все они не закончили?
РЕДАКТИРОВАТЬ # 1
Возвращаясь к тому, чего я хочу достичь: показывать индикатор обновления во время обновления представленияЯ придумал следующее (изменил функцию refresh
в ViewModel):
fun refresh() {
viewModelScope.launch {
try {
coroutineScope {
_refreshing.value = true
repository.accounts.value?.map { account ->
async {
repository.refresh(account.name)
}
}
}
} catch (cause: CancellationException) {
throw cause
} catch (cause: Exception) {
Timber.e(cause)
} finally {
_refreshing.value = false
}
}
}
ViewModel предоставляет LiveData, когда он обновляется, и фрагмент может наблюдать его, чтобы показать или скрыть счетчик. ,Это, кажется, делает трюк. Тем не менее, он все еще не чувствует себя хорошо, и я ценю любые улучшенные решения.