🎯 Рекомендуемые коллекции
Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать
Примеры Обработки Ошибок Android Kotlin
Примеры обработки ошибок Android Kotlin включая перехват исключений, логирование и проверку параметров
💻 Логирование kotlin
🟢 simple
⭐⭐⭐
Запись логов в файлы и консоль с различными уровнями и форматами
⏱️ 20 min
🏷️ kotlin, android, logging
Prerequisites:
Basic Kotlin, Android SDK
// Android Kotlin Logging Examples
// Using android.util.Log and file-based logging
import android.util.Log
import android.content.Context
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
// 1. Basic Android Logging
class BasicLogger {
companion object {
private const val TAG = "BasicLogger"
}
// Debug log
fun logDebug(message: String) {
Log.d(TAG, message)
}
// Info log
fun logInfo(message: String) {
Log.i(TAG, message)
}
// Warning log
fun logWarning(message: String) {
Log.w(TAG, message)
}
// Error log
fun logError(message: String) {
Log.e(TAG, message)
}
// Error log with exception
fun logError(message: String, throwable: Throwable) {
Log.e(TAG, message, throwable)
}
// Verbose log
fun logVerbose(message: String) {
Log.v(TAG, message)
}
// What a terrible failure log
fun logWtf(message: String) {
Log.wtf(TAG, message)
}
}
// 2. File-based Logging
class FileLogger(private val context: Context) {
private val logFile = File(context.filesDir, "app.log")
// Write log to file
fun writeLog(level: String, tag: String, message: String) {
try {
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
.format(Date())
val logEntry = "[$timestamp] [$level] [$tag] $message\n"
FileWriter(logFile, true).use { writer ->
writer.write(logEntry)
}
} catch (e: Exception) {
Log.e("FileLogger", "Failed to write log", e)
}
}
// Debug log to file
fun d(tag: String, message: String) {
writeLog("DEBUG", tag, message)
Log.d(tag, message)
}
// Info log to file
fun i(tag: String, message: String) {
writeLog("INFO", tag, message)
Log.i(tag, message)
}
// Warning log to file
fun w(tag: String, message: String) {
writeLog("WARN", tag, message)
Log.w(tag, message)
}
// Error log to file
fun e(tag: String, message: String) {
writeLog("ERROR", tag, message)
Log.e(tag, message)
}
// Error log with throwable
fun e(tag: String, message: String, throwable: Throwable) {
writeLog("ERROR", tag, "$message: ${throwable.message}")
Log.e(tag, message, throwable)
}
// Read log file
fun readLogs(): String {
return try {
if (logFile.exists()) {
logFile.readText()
} else {
"No log file found"
}
} catch (e: Exception) {
"Error reading logs: ${e.message}"
}
}
// Clear log file
fun clearLogs() {
try {
if (logFile.exists()) {
logFile.delete()
Log.i("FileLogger", "Log file cleared")
}
} catch (e: Exception) {
Log.e("FileLogger", "Failed to clear logs", e)
}
}
// Get log file size
fun getLogFileSize(): Long {
return if (logFile.exists()) {
logFile.length()
} else {
0L
}
}
}
// 3. Structured Logging
class StructuredLogger(private val context: Context) {
private val fileLogger = FileLogger(context)
private val tag = "StructuredLogger"
// Log structured data
fun logInfo(level: String, message: String, metadata: Map<String, Any?>) {
val metadataString = metadata.map { (key, value) ->
"$key=$value"
}.joinToString(", ")
val fullMessage = "$message [$metadataString]"
when (level.uppercase()) {
"DEBUG" -> fileLogger.d(tag, fullMessage)
"INFO" -> fileLogger.i(tag, fullMessage)
"WARN" -> fileLogger.w(tag, fullMessage)
"ERROR" -> fileLogger.e(tag, fullMessage)
}
}
// Log user action
fun logUserAction(action: String, userId: String?, details: Map<String, Any?>) {
logInfo("INFO", "User action: $action", mapOf(
"userId" to userId,
"timestamp" to System.currentTimeMillis(),
"details" to details.toString()
))
}
// Log API call
fun logApiCall(endpoint: String, method: String, statusCode: Int?, responseTime: Long) {
logInfo("INFO", "API call", mapOf(
"endpoint" to endpoint,
"method" to method,
"statusCode" to statusCode,
"responseTime" to responseTime
))
}
// Log error with context
fun logError(error: Throwable, context: Map<String, Any?>) {
val sw = StringWriter()
error.printStackTrace(PrintWriter(sw))
fileLogger.e(tag, "Error: ${error.message}\nContext: $context\nStack: ${sw.toString()}")
}
}
// 4. Logger with Levels
class LevelLogger(private val context: Context) {
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR }
private var currentLevel = LogLevel.INFO
fun setLevel(level: LogLevel) {
currentLevel = level
}
// Log at specified level
fun log(level: LogLevel, tag: String, message: String) {
if (level.ordinal >= currentLevel.ordinal) {
when (level) {
LogLevel.VERBOSE -> Log.v(tag, message)
LogLevel.DEBUG -> Log.d(tag, message)
LogLevel.INFO -> Log.i(tag, message)
LogLevel.WARN -> Log.w(tag, message)
LogLevel.ERROR -> Log.e(tag, message)
}
}
}
// Convenience methods
fun v(tag: String, message: String) = log(LogLevel.VERBOSE, tag, message)
fun d(tag: String, message: String) = log(LogLevel.DEBUG, tag, message)
fun i(tag: String, message: String) = log(LogLevel.INFO, tag, message)
fun w(tag: String, message: String) = log(LogLevel.WARN, tag, message)
fun e(tag: String, message: String) = log(LogLevel.ERROR, tag, message)
}
// 5. Crash Reporting Logger
class CrashReportingLogger(private val context: Context) {
private val crashFile = File(context.filesDir, "crash.log")
// Capture uncaught exception
fun captureException(thread: Thread, throwable: Throwable) {
try {
val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
.format(Date())
val report = StringBuilder()
report.appendLine("=== CRASH REPORT ===")
report.appendLine("Timestamp: $timestamp")
report.appendLine("Thread: ${thread.name}")
report.appendLine("Exception: ${throwable.javaClass.simpleName}")
report.appendLine("Message: ${throwable.message}")
report.appendLine("\nStack Trace:")
report.appendLine(getStackTrace(throwable))
// Log to file
FileWriter(crashFile, true).use { writer ->
writer.write(report.toString())
writer.write("\n\n")
}
Log.e("CrashReporter", "Crash captured", throwable)
} catch (e: Exception) {
Log.e("CrashReporter", "Failed to capture crash", e)
}
}
// Get stack trace as string
private fun getStackTrace(throwable: Throwable): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
throwable.printStackTrace(pw)
return sw.toString()
}
// Read crash reports
fun readCrashReports(): String {
return try {
if (crashFile.exists()) {
crashFile.readText()
} else {
"No crash reports found"
}
} catch (e: Exception) {
"Error reading crash reports: ${e.message}"
}
}
// Clear crash reports
fun clearCrashReports() {
try {
if (crashFile.exists()) {
crashFile.delete()
}
} catch (e: Exception) {
Log.e("CrashReporter", "Failed to clear crash reports", e)
}
}
}
// 6. Performance Logging
class PerformanceLogger(private val context: Context) {
private val fileLogger = FileLogger(context)
private val tag = "PerformanceLogger"
// Measure execution time
fun <T> measureTime(tag: String, operation: String, block: () -> T): T {
val startTime = System.nanoTime()
return try {
val result = block()
val endTime = System.nanoTime()
val durationMs = (endTime - startTime) / 1_000_000.0
fileLogger.i(tag, "$operation completed in ${String.format("%.2f", durationMs)}ms")
result
} catch (e: Exception) {
val endTime = System.nanoTime()
val durationMs = (endTime - startTime) / 1_000_000.0
fileLogger.e(tag, "$operation failed after ${String.format("%.2f", durationMs)}ms: ${e.message}")
throw e
}
}
// Log memory usage
fun logMemoryUsage(tag: String, context: String) {
val runtime = Runtime.getRuntime()
val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
val maxMemory = runtime.maxMemory() / 1024 / 1024
val totalMemory = runtime.totalMemory() / 1024 / 1024
fileLogger.i(tag, "Memory [$context]: Used=${usedMemory}MB, Total=${totalMemory}MB, Max=${maxMemory}MB")
}
// Start a timer
fun startTimer(operation: String): Timer {
return Timer(operation)
}
// Timer class
inner class Timer(private val operation: String) {
private val startTime = System.nanoTime()
fun end() {
val endTime = System.nanoTime()
val durationMs = (endTime - startTime) / 1_000_000.0
fileLogger.i(tag, "$operation took ${String.format("%.2f", durationMs)}ms")
}
}
}
// 7. Remote Logging (Simplified)
class RemoteLogger(private val context: Context) {
private val fileLogger = FileLogger(context)
// Queue logs for remote upload
private val logQueue = mutableListOf<String>()
// Add log to queue
fun queueLog(level: String, tag: String, message: String) {
val logEntry = mapOf(
"timestamp" to System.currentTimeMillis(),
"level" to level,
"tag" to tag,
"message" to message
)
synchronized(logQueue) {
logQueue.add(logEntry.toString())
}
// Keep queue size manageable
if (logQueue.size > 1000) {
logQueue.removeAt(0)
}
}
// Upload logs (simulated)
fun uploadLogs(): Boolean {
return try {
synchronized(logQueue) {
if (logQueue.isNotEmpty()) {
println("Uploading ${logQueue.size} log entries...")
// In real app, send to remote server
logQueue.clear()
true
} else {
false
}
}
} catch (e: Exception) {
fileLogger.e("RemoteLogger", "Upload failed: ${e.message}")
false
}
}
// Get queue size
fun getQueueSize(): Int {
return synchronized(logQueue) {
logQueue.size
}
}
}
// Main demonstration
fun demonstrateLogging(context: Context) {
println("=== Android Kotlin Logging Examples ===\n")
// 1. Basic logging
println("--- 1. Basic Android Logging ---")
val basicLogger = BasicLogger()
basicLogger.logVerbose("Verbose message")
basicLogger.logDebug("Debug message")
basicLogger.logInfo("Info message")
basicLogger.logWarning("Warning message")
basicLogger.logError("Error message")
try {
throw RuntimeException("Test exception")
} catch (e: Exception) {
basicLogger.logError("Caught exception", e)
}
// 2. File logging
println("\n--- 2. File Logging ---")
val fileLogger = FileLogger(context)
fileLogger.d("FileLog", "Debug log to file")
fileLogger.i("FileLog", "Info log to file")
fileLogger.w("FileLog", "Warning log to file")
fileLogger.e("FileLog", "Error log to file")
println("Log file size: ${fileLogger.getLogFileSize()} bytes")
// 3. Structured logging
println("\n--- 3. Structured Logging ---")
val structuredLogger = StructuredLogger(context)
structuredLogger.logUserAction("login", "user123", mapOf(
"ip" to "192.168.1.1",
"userAgent" to "Mozilla/5.0"
))
structuredLogger.logApiCall("/api/users", "GET", 200, 125)
try {
throw RuntimeException("API error")
} catch (e: Exception) {
structuredLogger.logError(e, mapOf("endpoint" to "/api/users"))
}
// 4. Level logging
println("\n--- 4. Level Logging ---")
val levelLogger = LevelLogger(context)
levelLogger.setLevel(LevelLogger.LogLevel.WARN)
levelLogger.v("LevelLog", "This won't be logged")
levelLogger.d("LevelLog", "This won't be logged")
levelLogger.i("LevelLog", "This won't be logged")
levelLogger.w("LevelLog", "This will be logged")
levelLogger.e("LevelLog", "This will be logged")
// 5. Performance logging
println("\n--- 5. Performance Logging ---")
val perfLogger = PerformanceLogger(context)
perfLogger.logMemoryUsage("PerfLog", "Before operation")
val result = perfLogger.measureTime("PerfLog", "Test operation") {
Thread.sleep(100)
"Operation result"
}
println("Result: $result")
perfLogger.logMemoryUsage("PerfLog", "After operation")
// Using timer
val timer = perfLogger.startTimer("Manual timer")
Thread.sleep(50)
timer.end()
// 6. Remote logging
println("\n--- 6. Remote Logging ---")
val remoteLogger = RemoteLogger(context)
remoteLogger.queueLog("INFO", "RemoteLog", "Log entry 1")
remoteLogger.queueLog("ERROR", "RemoteLog", "Log entry 2")
remoteLogger.queueLog("WARN", "RemoteLog", "Log entry 3")
println("Queue size: ${remoteLogger.getQueueSize()}")
remoteLogger.uploadLogs()
println("Queue size after upload: ${remoteLogger.getQueueSize()}")
println("\n=== All Logging Examples Completed ===")
}
💻 Проверка Параметров kotlin
🟢 simple
⭐⭐
Проверка параметров функции с использованием require, check и пользовательских валидаторов
⏱️ 15 min
🏷️ kotlin, android, validation
Prerequisites:
Basic Kotlin
// Android Kotlin Parameter Validation Examples
// Using Kotlin's built-in validation features
// 1. Basic Validation with require()
class BasicValidation {
// Validate positive number
fun calculateDiscount(price: Double, discount: Double): Double {
require(price > 0) { "Price must be positive, was: $price" }
require(discount in 0.0..100.0) { "Discount must be between 0 and 100, was: $discount" }
return price * (1 - discount / 100)
}
// Validate string parameters
fun createUsername(firstName: String, lastName: String): String {
require(firstName.isNotBlank()) { "First name cannot be blank" }
require(lastName.isNotBlank()) { "Last name cannot be blank" }
return "${firstName.lowercase()}_${lastName.lowercase()}"
}
// Validate collection
fun getFirstElement(list: List<String>): String {
require(list.isNotEmpty()) { "List cannot be empty" }
return list.first()
}
// Validate range
fun setVolume(volume: Int): Int {
require(volume in 0..100) { "Volume must be between 0 and 100, was: $volume" }
return volume
}
// Validate with multiple conditions
fun processUser(name: String, age: Int, email: String): Boolean {
require(name.isNotBlank()) { "Name cannot be blank" }
require(age in 18..120) { "Age must be between 18 and 120, was: $age" }
require(email.contains("@")) { "Email must contain '@'" }
println("User validated: $name, $age, $email")
return true
}
}
// 2. State Validation with check()
class StateValidation {
private var initialized = false
private var connected = false
// Initialize before use
fun initialize() {
initialized = true
println("Initialized")
}
// Connect to server
fun connect() {
check(initialized) { "Must initialize before connecting" }
connected = true
println("Connected")
}
// Send data
fun sendData(data: String) {
check(connected) { "Must be connected to send data" }
println("Data sent: $data")
}
// Check state before operation
fun performOperation() {
check(initialized) { "Not initialized" }
check(connected) { "Not connected" }
println("Operation performed")
}
}
// 3. Custom Validation Functions
class CustomValidators {
// Validate email format
fun isValidEmail(email: String): Boolean {
val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
return emailRegex.matches(email)
}
// Validate and return or throw
fun validateEmail(email: String): String {
require(isValidEmail(email)) { "Invalid email format: $email" }
return email
}
// Validate phone number
fun isValidPhoneNumber(phone: String): Boolean {
val phoneRegex = Regex("^\\+?[1-9]\\d{1,14}$")
return phoneRegex.matches(phone)
}
// Validate URL
fun isValidUrl(url: String): Boolean {
try {
java.net.URL(url).toURI()
return true
} catch (e: Exception) {
return false
}
}
// Validate password strength
enum class PasswordStrength { WEAK, MEDIUM, STRONG }
fun checkPasswordStrength(password: String): PasswordStrength {
if (password.length < 8) {
return PasswordStrength.WEAK
}
val hasUpperCase = password.any { it.isUpperCase() }
val hasLowerCase = password.any { it.isLowerCase() }
val hasDigit = password.any { it.isDigit() }
val hasSpecial = password.any { !it.isLetterOrDigit() }
return when {
hasUpperCase && hasLowerCase && hasDigit && hasSpecial -> PasswordStrength.STRONG
hasUpperCase && hasLowerCase && hasDigit -> PasswordStrength.MEDIUM
else -> PasswordStrength.WEAK
}
}
// Validate password with minimum strength
fun validatePassword(password: String, minStrength: PasswordStrength = PasswordStrength.MEDIUM): String {
val strength = checkPasswordStrength(password)
require(strength.ordinal >= minStrength.ordinal) {
"Password strength must be at least $minStrength, was: $strength"
}
return password
}
// Validate date range
fun isValidDateRange(start: java.util.Date, end: java.util.Date): Boolean {
return start.before(end) || start.equals(end)
}
// Validate and sanitize string
fun sanitizeAndValidate(input: String, maxLength: Int = 100): String {
val sanitized = input.trim()
require(sanitized.isNotEmpty()) { "Input cannot be empty" }
require(sanitized.length <= maxLength) { "Input exceeds maximum length of $maxLength" }
return sanitized
}
}
// 4. Validation with Error Collection
class ValidationCollector {
// Collect multiple validation errors
fun validateUser(data: Map<String, Any?>): ValidationResult {
val errors = mutableListOf<String>()
val name = data["name"] as? String
if (name.isNullOrBlank()) {
errors.add("Name is required")
}
val age = data["age"] as? Int
if (age == null) {
errors.add("Age is required")
} else if (age !in 18..120) {
errors.add("Age must be between 18 and 120")
}
val email = data["email"] as? String
if (email.isNullOrBlank()) {
errors.add("Email is required")
} else if (!email.contains("@")) {
errors.add("Email must contain '@'")
}
return ValidationResult(
isValid = errors.isEmpty(),
errors = errors
)
}
// Validation result data class
data class ValidationResult(
val isValid: Boolean,
val errors: List<String>
) {
fun getErrorString(): String {
return errors.joinToString("; ")
}
}
// Validate with early return
fun validateUserEarlyReturn(data: Map<String, Any?>): ValidationResult {
val name = data["name"] as? String
if (name.isNullOrBlank()) {
return ValidationResult(false, listOf("Name is required"))
}
val age = data["age"] as? Int
if (age == null || age !in 18..120) {
return ValidationResult(false, listOf("Age must be between 18 and 120"))
}
return ValidationResult(true, emptyList())
}
}
// 5. Business Logic Validation
class BusinessValidators {
// Validate payment amount
fun validatePaymentAmount(amount: Double, balance: Double): Boolean {
require(amount > 0) { "Payment amount must be positive" }
require(amount <= balance) { "Insufficient funds: balance=$balance, amount=$amount" }
return true
}
// Validate inventory
fun validateInventory(itemId: String, quantity: Int, stock: Int): Boolean {
require(quantity > 0) { "Quantity must be positive" }
require(quantity <= stock) { "Insufficient stock: available=$stock, requested=$quantity" }
return true
}
// Validate order
fun validateOrder(items: Map<String, Int>, stock: Map<String, Int>): Boolean {
require(items.isNotEmpty()) { "Order cannot be empty" }
for ((itemId, quantity) in items) {
val availableStock = stock[itemId] ?: 0
require(quantity <= availableStock) {
"Item $itemId: insufficient stock (available=$availableStock, requested=$quantity)"
}
}
return true
}
// Validate shipping address
data class Address(
val street: String,
val city: String,
val state: String,
val zipCode: String,
val country: String
)
fun validateAddress(address: Address): Boolean {
require(address.street.isNotBlank()) { "Street cannot be blank" }
require(address.city.isNotBlank()) { "City cannot be blank" }
require(address.state.isNotBlank()) { "State cannot be blank" }
require(address.zipCode.isNotBlank()) { "Zip code cannot be blank" }
require(address.country.isNotBlank()) { "Country cannot be blank" }
// Validate zip code format (US example)
val zipRegex = Regex("^\\d{5}(-\\d{4})?$")
require(zipRegex.matches(address.zipCode)) { "Invalid zip code format" }
return true
}
}
// 6. Validation Utils
class ValidationUtils {
// Generic range validator
fun <T : Comparable<T>> validateRange(
value: T,
min: T,
max: T,
paramName: String
): T {
require(value in min..max) {
"$paramName must be between $min and $max, was: $value"
}
return value
}
// Validate not null
fun <T> requireNotNull(value: T?, paramName: String): T {
return requireNotNull(value) { "$paramName cannot be null" }
}
// Validate not blank
fun requireNotBlank(value: String?, paramName: String): String {
require(!value.isNullOrBlank()) { "$paramName cannot be blank" }
return value
}
// Validate positive
fun requirePositive(value: Int, paramName: String): Int {
require(value > 0) { "$paramName must be positive, was: $value" }
return value
}
fun requirePositive(value: Double, paramName: String): Double {
require(value > 0) { "$paramName must be positive, was: $value" }
return value
}
// Validate non-negative
fun requireNonNegative(value: Int, paramName: String): Int {
require(value >= 0) { "$paramName must be non-negative, was: $value" }
return value
}
// Validate size
fun <T> validateSize(
collection: Collection<T>,
minSize: Int,
maxSize: Int,
paramName: String
): Collection<T> {
require(collection.size in minSize..maxSize) {
"$paramName size must be between $minSize and $maxSize, was: ${collection.size}"
}
return collection
}
}
// Main demonstration
fun demonstrateParameterValidation() {
println("=== Android Kotlin Parameter Validation Examples ===\n")
// 1. Basic validation
println("--- 1. Basic Validation with require() ---")
val basicValidation = BasicValidation()
val discount = basicValidation.calculateDiscount(100.0, 15.0)
println("Discounted price: $$discount")
val username = basicValidation.createUsername("John", "Doe")
println("Username: $username")
try {
basicValidation.setVolume(150)
} catch (e: IllegalArgumentException) {
println("Validation error: ${e.message}")
}
// 2. State validation
println("\n--- 2. State Validation with check() ---")
val stateValidation = StateValidation()
stateValidation.initialize()
stateValidation.connect()
stateValidation.sendData("Hello")
try {
val stateValidation2 = StateValidation()
stateValidation2.sendData("Test")
} catch (e: IllegalStateException) {
println("State error: ${e.message}")
}
// 3. Custom validators
println("\n--- 3. Custom Validators ---")
val customValidators = CustomValidators()
val email = "[email protected]"
println("Is valid email: ${customValidators.isValidEmail(email)}")
val phone = "+1234567890"
println("Is valid phone: ${customValidators.isValidPhoneNumber(phone)}")
val url = "https://example.com"
println("Is valid URL: ${customValidators.isValidUrl(url)}")
val passwordStrength = customValidators.checkPasswordStrength("Pass123!")
println("Password strength: $passwordStrength")
// 4. Validation collector
println("\n--- 4. Validation Collector ---")
val collector = ValidationCollector()
val userData = mapOf(
"name" to "John Doe",
"age" to 25,
"email" to "[email protected]"
)
val result = collector.validateUser(userData)
println("Validation result: ${result.isValid}")
if (!result.isValid) {
println("Errors: ${result.getErrorString()}")
}
// 5. Business validation
println("\n--- 5. Business Logic Validation ---")
val businessValidators = BusinessValidators()
val paymentValid = businessValidators.validatePaymentAmount(50.0, 100.0)
println("Payment valid: $paymentValid")
val address = BusinessValidators.Address(
street = "123 Main St",
city = "New York",
state = "NY",
zipCode = "10001",
country = "USA"
)
val addressValid = businessValidators.validateAddress(address)
println("Address valid: $addressValid")
// 6. Validation utils
println("\n--- 6. Validation Utils ---")
val utils = ValidationUtils()
val validRange = utils.validateRange(50, 0, 100, "value")
println("Validated range: $validRange")
val notNull = utils.requireNotNull("value", "param")
println("Not null: $notNull")
val positive = utils.requirePositive(10, "count")
println("Positive: $positive")
val list = listOf(1, 2, 3)
val validList = utils.validateSize(list, 1, 5, "items")
println("Validated list size: ${validList.size}")
println("\n=== All Parameter Validation Examples Completed ===")
}
💻 Перехват Исключений kotlin
🟡 intermediate
⭐⭐⭐
Обработка исключений с использованием блоков try-catch, пользовательских исключений и стратегий восстановления ошибок
⏱️ 25 min
🏷️ kotlin, android, error handling
Prerequisites:
Intermediate Kotlin
// Android Kotlin Exception Catching Examples
// Using Kotlin exception handling mechanisms
// 1. Basic Exception Handling
class BasicExceptionHandling {
// Simple try-catch
fun divideNumbers(a: Int, b: Int): Double? {
return try {
(a.toDouble() / b.toDouble()).also {
println("Result: $it")
}
} catch (e: ArithmeticException) {
println("Division by zero: ${e.message}")
null
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
null
}
}
// Try-catch-finally
fun processFile(filePath: String): String? {
var content: String? = null
try {
val file = java.io.File(filePath)
content = file.readText()
println("File read successfully")
} catch (e: java.io.FileNotFoundException) {
println("File not found: ${e.message}")
} catch (e: java.io.IOException) {
println("IO error: ${e.message}")
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
} finally {
println("File processing attempt completed")
}
return content
}
// Try-catch with resource cleanup
fun processFileWithCleanup(filePath: String): String? {
val file = java.io.File(filePath)
return try {
if (file.exists()) {
file.readText().also {
println("Content length: ${it.length}")
}
} else {
println("File does not exist")
null
}
} catch (e: Exception) {
println("Error processing file: ${e.message}")
null
}
}
// Nested try-catch
fun nestedExceptionHandling() {
try {
println("Outer try block")
try {
println("Inner try block")
val result = 10 / 0
} catch (e: ArithmeticException) {
println("Inner catch: ${e.message}")
throw RuntimeException("Re-throwing from inner")
}
} catch (e: RuntimeException) {
println("Outer catch: ${e.message}")
}
}
}
// 2. Custom Exceptions
class CustomExceptions {
// Custom exception class
class ValidationException(message: String) : Exception(message)
class NetworkException(message: String, val statusCode: Int? = null) : Exception(message)
class DatabaseException(message: String, val errorCode: String) : Exception(message)
// Throw and catch custom exception
fun validateAge(age: Int) {
if (age < 0) {
throw ValidationException("Age cannot be negative: $age")
}
if (age > 150) {
throw ValidationException("Age exceeds maximum: $age")
}
println("Age $age is valid")
}
// Handle custom exception
fun processUserAge(age: Int): Boolean {
return try {
validateAge(age)
true
} catch (e: ValidationException) {
println("Validation failed: ${e.message}")
false
}
}
// Network operation with custom exception
fun fetchFromServer(endpoint: String): String? {
if (endpoint.isEmpty()) {
throw NetworkException("Endpoint cannot be empty", null)
}
// Simulate network call
return try {
// Simulated response
"Data from $endpoint"
} catch (e: Exception) {
throw NetworkException("Network error: ${e.message}", 500)
}
}
// Handle network exception
fun safeFetch(endpoint: String): String? {
return try {
fetchFromServer(endpoint)
} catch (e: NetworkException) {
println("Network error (status ${e.statusCode}): ${e.message}")
null
}
}
// Multiple custom exceptions
fun databaseOperation(operation: String): Boolean {
return try {
when (operation) {
"connect" -> true
"query" -> true
else -> throw DatabaseException("Unknown operation: $operation", "OP_UNKNOWN")
}
} catch (e: DatabaseException) {
println("DB error [${e.errorCode}]: ${e.message}")
false
}
}
}
// 3. Exception Chaining
class ExceptionChaining {
// Exception chaining with cause
fun loadDataFromFile(filePath: String): String {
return try {
val file = java.io.File(filePath)
file.readText()
} catch (e: java.io.IOException) {
throw RuntimeException("Failed to load data from file: $filePath", e)
}
}
// Catch and re-throw with additional context
fun processData(filePath: String): Int {
return try {
val data = loadDataFromFile(filePath)
data.length
} catch (e: RuntimeException) {
throw RuntimeException("Failed to process data from: $filePath", e)
}
}
// Handle chained exception
fun safeProcessData(filePath: String): Int? {
return try {
processData(filePath)
} catch (e: RuntimeException) {
println("Error: ${e.message}")
println("Cause: ${e.cause?.message}")
// Print stack trace of cause
e.cause?.printStackTrace()
null
}
}
// Add suppression
@Suppress("TooGenericExceptionCaught")
fun handleWithSuppression(): String? {
return try {
// Some operation
"Success"
} catch (e: Exception) {
println("Caught exception: ${e.message}")
null
}
}
}
// 4. Kotlin-specific Exception Handling
class KotlinExceptionHandling {
// Result type for error handling
fun divideSafely(a: Int, b: Int): Result<Double> {
return try {
Result.success(a.toDouble() / b.toDouble())
} catch (e: ArithmeticException) {
Result.failure(e)
}
}
// Use Result type
fun performDivision(): String? {
val result = divideSafely(10, 0)
return result.fold(
onSuccess = { value ->
println("Result: $value")
value.toString()
},
onFailure = { error ->
println("Error: ${error.message}")
null
}
)
}
// Nullable return type
fun parseToInt(value: String): Int? {
return try {
value.toInt()
} catch (e: NumberFormatException) {
println("Invalid number: $value")
null
}
}
// Elvis operator with exception
fun getNameOrThrow(map: Map<String, String?>): String {
return map["name"] ?: throw IllegalArgumentException("Name is required")
}
// Require for validation
fun processAmount(amount: Double) {
require(amount > 0) { "Amount must be positive, was: $amount" }
require(amount <= 1_000_000) { "Amount exceeds maximum" }
println("Amount processed: $$amount")
}
// Check for state validation
fun processPayment(balance: Double, amount: Double) {
check(balance >= amount) { "Insufficient funds: balance=$balance, required=$amount" }
println("Payment processed")
}
// RunCatching
fun safeOperation(): Int? {
val result = runCatching {
"10".toInt() * 2
}
return result.getOrNull()
}
// Multiple operations with runCatching
fun chainOperations(): String? {
val result = runCatching {
val num = "10".toInt()
val doubled = num * 2
"Result: $doubled"
}.onFailure { e ->
println("Operation failed: ${e.message}")
}
return result.getOrNull()
}
}
// 5. Advanced Error Recovery
class ErrorRecovery {
// Retry mechanism
fun <T> retryOperation(
operation: () -> T,
maxAttempts: Int = 3,
delayMs: Long = 1000
): T? {
var lastException: Exception? = null
repeat(maxAttempts) { attempt ->
try {
return operation()
} catch (e: Exception) {
lastException = e
println("Attempt ${attempt + 1} failed: ${e.message}")
if (attempt < maxAttempts - 1) {
Thread.sleep(delayMs)
}
}
}
println("All $maxAttempts attempts failed")
return null
}
// Fallback strategy
fun <T> tryWithFallback(
primary: () -> T,
fallback: () -> T
): T {
return try {
primary()
} catch (e: Exception) {
println("Primary failed, using fallback: ${e.message}")
fallback()
}
}
// Multiple fallbacks
fun <T> tryMultipleFallbacks(
operations: List<Pair<String, () -> T>>
): T? {
for ((name, operation) in operations) {
try {
val result = operation()
println("Success with: $name")
return result
} catch (e: Exception) {
println("Failed with $name: ${e.message}")
}
}
println("All operations failed")
return null
}
// Circuit breaker pattern (simplified)
class CircuitBreaker<T>(
private val operation: () -> T,
private val failureThreshold: Int = 3
) {
private var failureCount = 0
private var lastFailureTime: Long = 0
private var state = State.CLOSED
enum class State { CLOSED, OPEN, HALF_OPEN }
fun execute(): T? {
if (state == State.OPEN) {
val timeSinceFailure = System.currentTimeMillis() - lastFailureTime
if (timeSinceFailure > 60000) { // 1 minute cooldown
state = State.HALF_OPEN
println("Circuit breaker entering HALF_OPEN state")
} else {
println("Circuit breaker is OPEN, rejecting request")
return null
}
}
return try {
val result = operation()
if (state == State.HALF_OPEN) {
state = State.CLOSED
failureCount = 0
println("Circuit breaker reset to CLOSED")
}
result
} catch (e: Exception) {
failureCount++
lastFailureTime = System.currentTimeMillis()
if (failureCount >= failureThreshold) {
state = State.OPEN
println("Circuit breaker opened after $failureCount failures")
}
throw e
}
}
}
// Execute with circuit breaker
fun <T> executeWithCircuitBreaker(operation: () -> T): T? {
val circuitBreaker = CircuitBreaker(operation)
return try {
circuitBreaker.execute()
} catch (e: Exception) {
println("Circuit breaker operation failed: ${e.message}")
null
}
}
}
// 6. Exception Logging
class ExceptionLogger {
// Log exception with context
fun logException(context: String, exception: Exception) {
println("[$context] Exception: ${exception.javaClass.simpleName}")
println("[$context] Message: ${exception.message}")
println("[$context] Stack trace:")
exception.stackTrace.forEach { element ->
println("[$context] at $element")
}
}
// Log with custom severity
enum class Severity { ERROR, WARNING, INFO }
fun logWithSeverity(
severity: Severity,
message: String,
exception: Exception? = null
) {
val timestamp = java.util.Date()
println("[$timestamp] [$severity] $message")
exception?.let {
println("[$timestamp] [$severity] Caused by: ${it.message}")
}
}
// Collect exception details
fun captureExceptionDetails(exception: Exception): ExceptionDetails {
return ExceptionDetails(
type = exception.javaClass.simpleName,
message = exception.message,
cause = exception.cause?.message,
stackTrace = exception.stackTrace.joinToString("\n") { " at $it" }
)
}
// Exception details data class
data class ExceptionDetails(
val type: String,
val message: String?,
val cause: String?,
val stackTrace: String
)
}
// Main demonstration
fun demonstrateExceptionCatching() {
println("=== Android Kotlin Exception Catching Examples ===\n")
// 1. Basic exception handling
println("--- 1. Basic Exception Handling ---")
val basicHandler = BasicExceptionHandling()
basicHandler.divideNumbers(10, 2)
basicHandler.divideNumbers(10, 0)
basicHandler.processFile("nonexistent.txt")
// 2. Custom exceptions
println("\n--- 2. Custom Exceptions ---")
val customExceptions = CustomExceptions()
val ages = listOf(25, -5, 200, 30)
ages.forEach { age ->
val isValid = customExceptions.processUserAge(age)
println("Age $age valid: $isValid")
}
// 3. Exception chaining
println("\n--- 3. Exception Chaining ---")
val chaining = ExceptionChaining()
chaining.safeProcessData("nonexistent.txt")
// 4. Kotlin-specific handling
println("\n--- 4. Kotlin-Specific Handling ---")
val kotlinHandler = KotlinExceptionHandling()
kotlinHandler.performDivision()
kotlinHandler.parseToInt("123")
kotlinHandler.parseToInt("abc")
// 5. Error recovery
println("\n--- 5. Error Recovery ---")
val recovery = ErrorRecovery()
var attempts = 0
val result = recovery.retryOperation(
operation = {
attempts++
if (attempts < 3) {
throw RuntimeException("Simulated failure")
}
"Success after $attempts attempts"
},
maxAttempts = 5
)
println("Retry result: $result")
// 6. Fallback
val fallbackResult = recovery.tryWithFallback(
primary = { throw RuntimeException("Primary failed") },
fallback = { "Fallback value" }
)
println("Fallback result: $fallbackResult")
// 7. Exception logging
println("\n--- 6. Exception Logging ---")
val logger = ExceptionLogger()
try {
throw RuntimeException("Test exception")
} catch (e: Exception) {
logger.logException("TestContext", e)
}
logger.logWithSeverity(ExceptionLogger.Severity.ERROR, "Critical error occurred")
println("\n=== All Exception Catching Examples Completed ===")
}