Какие знаешь варианты обработки ошибок?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Подходы к обработке ошибок в Android-разработке
Обработка ошибок — системный подход к управлению исключительными ситуациями. В Android мы используем комбинацию парадигм.
1. Исключения (Exceptions)
Проверяемые (checked) и непроверяемые (unchecked) исключения. Первые обязательны для обработки, вторые (RuntimeException) обычно указывают на ошибки программирования.
// Проверяемое исключение
try {
val file = FileInputStream("file.txt")
} catch (e: FileNotFoundException) {
Log.e("App", "Файл не найден", e)
showErrorToast()
}
// Непроверяемое исключение
fun calculate(value: String): Int {
return try {
value.toInt() * 2 // Может выбросить NumberFormatException
} catch (e: NumberFormatException) {
Log.w("App", "Некорректный формат числа")
defaultValue
}
}
2. Возврат кодов ошибок
Традиционный подход, особенно в нативных библиотеках и низкоуровневых операциях.
fun saveData(data: String): ResultCode {
return if (writeToStorage(data)) {
ResultCode.SUCCESS
} else {
ResultCode.WRITE_ERROR
}
}
enum class ResultCode {
SUCCESS,
WRITE_ERROR,
NETWORK_ERROR,
VALIDATION_ERROR
}
3. Функциональный подход с Result<T>
Современный паттерн, популярный в Kotlin, который инкапсулирует результат или ошибку.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
fun fetchUserData(userId: String): Result<User> {
return try {
val user = apiService.getUser(userId)
Result.Success(user)
} catch (e: IOException) {
Result.Error(e)
}
}
// Использование
when (val result = fetchUserData("123")) {
is Result.Success -> updateUI(result.data)
is Result.Error -> showErrorDialog(result.exception)
}
4. LiveData/StateFlow с состояниями загрузки
Для UI-слоя в архитектурах типа MVVM, MVI.
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<out T>(val data: T) : UiState<T>()
data class Error(val message: String, val retryAction: () -> Unit) : UiState<Nothing>()
}
class UserViewModel : ViewModel() {
private val _userState = MutableStateFlow<UiState<User>>(UiState.Loading)
val userState: StateFlow<UiState<User>> = _userState.asStateFlow()
fun loadUser() {
viewModelScope.launch {
_userState.value = UiState.Loading
try {
val user = repository.getUser()
_userState.value = UiState.Success(user)
} catch (e: Exception) {
_userState.value = UiState.Error(
message = "Не удалось загрузить пользователя",
retryAction = { loadUser() }
)
}
}
}
}
5. Callback-функции с ошибками
Для асинхронных операций, особенно в legacy-коде.
interface DataCallback<T> {
fun onSuccess(data: T)
fun onError(error: Throwable)
}
fun loadData(callback: DataCallback<String>) {
thread {
try {
val data = performNetworkRequest()
callback.onSuccess(data)
} catch (e: Exception) {
callback.onError(e)
}
}
}
6. Корутины с исключениями
Structured Concurrency в Kotlin корутинах предоставляет мощные механизмы.
viewModelScope.launch(
CoroutineExceptionHandler { _, throwable ->
// Централизованная обработка
handleError(throwable)
}
) {
try {
val data = withContext(Dispatchers.IO) {
repository.fetchData() // Может выбрасывать исключения
}
processData(data)
} catch (e: NetworkException) {
// Специфичная обработка
showNetworkError(e)
} catch (e: ValidationException) {
showValidationError(e)
}
}
7. Контракты и валидация
Предотвращение ошибок через проверку предусловий.
fun updateProfile(name: String, age: Int) {
require(name.isNotBlank()) { "Имя не может быть пустым" }
require(age in 1..120) { "Некорректный возраст" }
// Основная логика
repository.saveProfile(name, age)
}
Ключевые принципы
- Fail-fast: Быстрое выявление ошибок
- Graceful degradation: Плавная деградация функционала
- User-friendly messages: Понятные сообщения пользователю
- Logging и мониторинг: Логирование для диагностики
- Локализация ошибок: Обработка близко к источнику проблемы
- Retry-логика: Стратегии повторных попыток для временных сбоев
Выбор подхода зависит от контекста: для UI удобны StateFlow/LiveData с sealed-классами, для бизнес-логики — Result-типы, для асинхронных операций — корутины с обработчиками исключений. Важно сочетать защитное программирование с четкой коммуникацией ошибок пользователю. В современных приложениях часто комбинируют несколько подходов на разных уровнях приложения.