Как реализовать запрос на сервер при изменении текста с обработкой ошибок
Что проверяют
Умеете ли вы превратить поток нажатий в разумное число запросов и не показывать устаревшие ответы. Повесить TextWatcher и вызвать Retrofit умеют все, интервьюера интересуют debounce, отмена предыдущего запроса и место, где живёт состояние. Следом спрашивают, что станет с запросом при повороте экрана.
Как отвечать
Логика во ViewModel, экран только передаёт текст и рисует состояние:
- источник:
MutableStateFlow<String>с запросом. В Compose его обновляетonValueChangeуTextField, во View-кодеdoAfterTextChangedиз core-ktx; - цепочка:
query.debounce(300).map { it.trim() }.filter { it.length >= 2 }.distinctUntilChanged().flatMapLatest { search(it) }.debounceждёт паузы в наборе,flatMapLatestотменяет корутину прошлого запроса, когда пришёл новый текст; - состояние: sealed interface с
Loading,Success(items),Empty,Error(message), собранный черезstateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initial).ViewModelпереживает поворот, запрос не повторяется; - ошибки:
suspend-метод Retrofit бросаетHttpExceptionна ответ вне 2xx иIOExceptionна сетевой сбой. Их переводят в понятный текст: «нет сети», «сервер недоступен», для 401 свой сценарий; - повтор: кнопка «Повторить» или
retryс задержкой только дляIOException.
Ошибку ловят внутри flatMapLatest, в потоке одного запроса: flow { emit(api.search(q)) }.map { ... }.catch { emit(Error(...)) }.
Где спотыкаются
Шлют запрос на каждый символ и не отменяют предыдущий. Ответ на «ja» приходит позже ответа на «java» и перезаписывает список.
Ставят catch на внешний поток. Первая ошибка завершает всю цепочку, и дальше ввод текста ничего не делает до пересоздания экрана.
Оборачивают вызов в runCatching или catch (e: Exception). Так перехватывается и CancellationException: отмена перестаёт работать как отмена, а в UI мигает ошибка на каждом новом символе.