Android. Coroutine. Retrofit 2. Handler Error
Подскажите, пожалуйста, хороший материал для изучения, который показывает как правильно обрабатывать исключения в retrofit 2 + coroutine.
scope.launch {
try {
val response : Response<AuthResponse>
withContext(Dispatchers.IO) {
response = postAPI.setAuth(key, id, authRequest)
}
if (response.isSuccessful) {
view.onSuccessful()
} else {
when (JsonToErrorMessage.getErrorMessage(response.errorBody()!!)) {
ErrorResponse.WRONG_CODE -> {
view.onWrongCode()
}
ErrorResponse.CODE_TIME_OUT -> {
view.onTimeout()
}
else -> {
view.onUnknownError()
}
}
}
} catch (e: IOException) {
view.onNetworkError()
} catch (e: Exception) {
view.onUnknownError()
}
}
Каждый раз писать такое, подсказывает мне, что это неправильно, хотя достаточно много примеров, где так и делается. Да и к тому же каждый раз дублирование кода.
Ответы (1 шт):
Ну корутины или не корутины, вас все равно придется что-то такое писать. В той же RX Java вы в методе subscribe() постоянно обрабатываете onError(). Но сделать это красивее можно.
Я покажу вам 1 пример.
Сперва вам нужен класс
Result, некая сущность которая отвечает за результат. В Котлине есть прекрасная штукаsealed class, что-то вроде такого:sealed class Result<out T> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Exception) : Result<Nothing>() }В нем есть 2 состояния:
Success - когда данные пришли и все ок
Error - в случае ошибки
Так же вы можете добавить свои состояния. Можно к примеру разные типы ошибок возвращать в разных состояниях. Или к примеру добавить состояние
Loading. Тут по своим потребностям.
После чего можно создать базовый класс вроде такого:
abstract class BaseRemoteProvider<RESPONSE>() : NetworkProvider<RESPONSE> { abstract suspend fun getResponse(): Response<RESPONSE> override suspend fun get(): Result<RESPONSE> { return try { val result = parseResponse(getResponse()) Result.Success(result) } catch (e: UnknownHostException) { Result.Error(e) } catch (e: ConnectException) { Result.Error(e) } catch (e: Exception) { Result.Error(e) } } private fun parseResponse(response: Response<RESPONSE>): RESPONSE { val data: RESPONSE? if (response.isSuccessful) data = response.body() else throw getNetworkException(response) if (data != null) return data else throw NetworkException(statusMessage = "Generated Network Error Something went wrong") } private fun getNetworkException(response: Response<RESPONSE>): NetworkException { val type = object : TypeToken<NetworkException>() {}.type return Gson().fromJson(response.errorBody()!!.charStream(), type) } }Что здесь происходит? Мы отправляем запрос на сервер и проверяем его. Если с ответом все ок, то мы оборачиваем этот ответ в наш
Result.Successи возвращаем его дальше. Если какая-то ошибка - мы оборачиваем вResuslt.Error. NetworkExceprion это моя кастомная ошибка, которую отдает бек и методgetNetworkException(response)как раз мапит ответ в объект.Вот сам
NetworkExceprionдля наглядности:data class NetworkException( @SerializedName("status_code") val statusCode: Int = 0, @SerializedName("status_message") val statusMessage: String = "", @SerializedName("success") val isSuccessful: Boolean = false ) : Exception()Собственно это все. В вашей ViewModel или любом другом месте вы обрабатываете это так:
fun loadData() { backgroundScope.launch { when (val result = remoteProvider.get()) { is Result.Success -> handleData(result.data.items) is Result.Error -> handleError(result.exception) } } }