¿Cómo hacer que Android «lance los dados»? Analizamos la generación de números aleatorios en Kotlin
\n\n¿Alguna vez te has preguntado cómo tu teléfono «elige» un número aleatorio? Parece una tarea simple: obtener un entero aleatorio del 5 al 10. Pero bajo el capó de Android y Kotlin se esconde todo un mecanismo del que depende no solo la comodidad, sino también la seguridad de tu aplicación. No es de extrañar que la pregunta «How can I generate random number in specific range in Android?» haya acumulado miles de visitas en Stack Overflow. No solo daremos una respuesta preparada: nos adentraremos en el código fuente y entenderemos qué sucede realmente.
\n\nEn este artículo aprenderás:
\n- \n
- Cómo generar correctamente un número aleatorio en un rango específico en Kotlin. \n
- En qué se diferencian
kotlin.random.Random,java.util.RandomySecureRandom. \n - Por qué para juegos de azar se necesita una clase y para generar contraseñas, otra. \n
- Cómo evitar errores típicos que convierten la «aleatoriedad» en predecibilidad. \n
¡Allá vamos!
\n\nClásico del género: Random en Kotlin (y por qué no siempre es correcto)
\n\nLa forma más obvia es usar kotlin.random.Random. Está integrado directamente en el lenguaje y funciona de inmediato. Así es como se ve la generación de un número aleatorio del 0 al 100 (inclusive):
import kotlin.random.Random\n\nfun main() {\n val randomNumber = Random.nextInt(0, 101) // del 0 al 100 inclusive\n println("Número aleatorio: $randomNumber")\n}\n\nTodo es simple. Pero echemos un vistazo bajo el capó. Random.nextInt(from, until) internamente usa java.util.Random (en la plataforma JVM). Y este, a su vez, genera números mediante un generador lineal congruencial (LCG). La fórmula es simple:
new_seed = (old_seed * A + C) mod M
Donde A, C y M son constantes. Es rápido, pero no es criptográficamente seguro. Si un atacante descubre aunque sea algunos números, puede recuperar la semilla y predecir todos los siguientes.
\n\nCuándo usarlo: Para juegos (no de dinero), selección aleatoria de un elemento de una lista, barajar tarjetas en una aplicación normal.
\n\nNivel avanzado: ThreadLocalRandom para concurrencia
\n\nSi tu aplicación usa activamente corrutinas o subprocesos múltiples, el Random común puede convertirse en un cuello de botella. Cada llamada a nextInt() se sincroniza para evitar condiciones de carrera. La solución es ThreadLocalRandom de Java:
import java.util.concurrent.ThreadLocalRandom\n\nfun getRandomInRange(min: Int, max: Int): Int {\n return ThreadLocalRandom.current().nextInt(min, max + 1)\n}\n\nEsta clase asigna una instancia separada de Random para cada hilo. No se necesita sincronización: cada hilo trabaja con su propio generador. En la práctica, esto proporciona un aumento de rendimiento en escenarios de subprocesos múltiples.
La seguridad ante todo: SecureRandom
\n\nAhora imagina que estás generando una contraseña de un solo uso (OTP) o un token para recuperar el acceso. Usar el Random común es un delito. Aquí entra en juego SecureRandom:
import java.security.SecureRandom\n\nfun generateSecureCode(length: Int): String {\n val secureRandom = SecureRandom()\n val digits = "0123456789"\n return (1..length)\n .map { digits[secureRandom.nextInt(digits.length)] }\n .joinToString("")\n}\n\nSecureRandom recoge entropía de fuentes del sistema operativo: ruido del micrófono, tiempos de pulsaciones de teclas (en el emulador, del archivo /dev/random o /dev/urandom). Esto lo hace impredecible. Pero esto tiene un costo en velocidad: puede ser decenas de veces más lento que el Random común.
Importante: En Android, S