Exemples de Gestion des Erreurs Android Kotlin

Exemples de gestion des erreurs Android Kotlin incluant la capture d'exceptions, la journalisation et la validation des paramètres

Key Facts

Category
Kotlin
Items
3
Format Families
audio

Sample Overview

Exemples de gestion des erreurs Android Kotlin incluant la capture d'exceptions, la journalisation et la validation des paramètres This sample set belongs to Kotlin and can be used to test related workflows inside Elysia Tools.

💻 Journalisation kotlin

🟢 simple ⭐⭐⭐

Écrire des logs dans des fichiers et la console avec divers niveaux et formats

⏱️ 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 ===")
}

💻 Validation des Paramètres kotlin

🟢 simple ⭐⭐

Valider les paramètres de fonction en utilisant require, check et des validateurs personnalisés

⏱️ 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 ===")
}

💻 Capture d'Exceptions kotlin

🟡 intermediate ⭐⭐⭐

Gérer les exceptions en utilisant des blocs try-catch, des exceptions personnalisées et des stratégies de récupération d'erreurs

⏱️ 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 {
            if (b == 0) {
                throw ArithmeticException("Division by zero")
            }
            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 ===")
}