У меня проблема с попыткой выяснить лучший способ реализации UseCase с прогрессом. Я видел такие примеры:
Приложение Google I / O для Android https://github.com/google/iosched/blob/master/shared/src/main/java/com/google/samples/apps/iosched/shared/domain/UseCase.kt
Что мне не нравится, так это:
- Выполучить, чтобы получить результат в пользовательском интерфейсе и принимать там много решений.
- Нет прогресса
И что-то, что я вижу в качестве обновления до вышеупомянутогоэто:
Чертежи архитектуры Android
https://github.com/android/architecture-samples/blob/usecases/app/src/main/java/com/example/android/architecture/blueprints/todoapp/domain/ActivateTasksUseCase.kt
Они используют сопрограммы, и теперь результат находится во ViewModel, и он намного лучше. Но опять же: Нет прогресса
Моя проблема здесь в том, что все используют RxJava, потому что все остальные используют его. Я вижу, что многие люди думают, что это то, что "выполняет задачи в фоновом режиме". Но это слишком много для меня. Мне это не нужно
Я видел несколько примеров с каналами Coroutines, но они действительно ужасны.
Так недавно я наткнулся на эту статью Романа Елизарова:
https://medium.com/@elizarov/callbacks-and-kotlin-flows-2b53aa2525cf
Я сделал что-то вроде этого:
class ProduceValueWithProgressUseCase @Inject constructor(
private val executor: Executor
) {
operator fun invoke(): Flow<Result<Int, Int>> {
return callbackFlow {
val callback = object : CallbackProgress {
override fun onProgress(result: Int) {
offer(Result.Loading(result))
}
override fun onSuccess(result: Int) {
offer(Result.Success(result))
close()
}
override fun onError(e: Exception) {
offer(Result.Error(e))
}
}
val producer = ValueWithProgressProducer(callback)
executor.execute(producer)
awaitClose {}
}
}
}
Идея состоит в том, что этот «производитель» использует обратный вызов для распространения данных, как большинство «старых» API. Поэтому я хочу распространить код через поток в ViewModel и не помещать туда обратные вызовы. Вот так:
viewModelScope.launch {
produceValueWithProgressUseCase().collect {
when (it) {
is Success -> view.showWithProgressResult(it.data)
is Loading -> view.updateProgress(it.progress)
else -> view.showError()
}
}
}
Так что да, в основном API-интерфейс Flows сделает всю работу за меня. Я создал даже небольшое приложение для тестирования, где я просто генерирую числа, и оно работало нормально. Что мне не понравилось в этом, так это:
Слишком много аннотаций ExperimentalCoroutinesApi. Например, здесь (извините за форматирование):
@ Suppress ("NOTHING_TO_INLINE") @ExperimentalCoroutinesApi public inline fun callbackFlow (@BuilderInference noinline block: suspend ProducerScope. () -> Unit): Flow = channelFlow (block))
Приведенный выше код является частью файла: kotlinx.coroutines.flow.Builders.kt из версии: kotlinx-coroutines-core-1.3.2
- В какой-то момент я кое-что ударил аннотацией @Preview. (Если честно, не помню, где. Это было то, что я удалил.)
- Я тоже немного попробовал посмотреть, кактестирование будет идти, но это не так просто. То же самое можно увидеть в коде от Blueprinst.
- Я также смешиваю код, который выполняет задачу, и саму задачу. Я имею в виду использование callbackFlow ().
Итак, в конце я вижу нечто, похожее на призыв к переменам в следующем году. Поэтому, пожалуйста, выскажите свои мысли по этому поводу.