Comment faire « jeter les dés » à Android ? Décryptage de la génération de nombres aléatoires avec Kotlin
\n\nAvez-vous déjà réfléchi à la façon dont votre téléphone « choisit » un nombre aléatoire ? Apparemment une tâche simple — obtenir un entier aléatoire entre 5 et 10. Mais sous le capot d'Android et de Kotlin se cache tout un mécanisme, dont dépendent non seulement la commodité, mais aussi la sécurité de votre application. Il n'est pas surprenant que la question « How can I generate random number in specific range in Android? » ait recueilli des milliers de vues sur Stack Overflow. Nous ne nous contenterons pas de donner une réponse toute faite — nous allons jeter un coup d'œil dans les sources et comprendre ce qui se passe réellement.
\n\nDans cet article, vous apprendrez :
\n- \n
- Comment générer correctement un nombre aléatoire dans une plage donnée avec Kotlin. \n
- En quoi
kotlin.random.Random,java.util.RandometSecureRandomdiffèrent. \n - Pourquoi les jeux de hasard nécessitent une classe, et la génération de mots de passe une autre. \n
- Comment éviter les erreurs typiques qui transforment le « hasard » en prévisibilité. \n
C'est parti !
\n\nLe classique du genre : Random en Kotlin (et pourquoi ce n'est pas toujours correct)
\n\nLa méthode la plus évidente est d'utiliser kotlin.random.Random. Il est intégré directement dans le langage et fonctionne prêt à l'emploi. Voici à quoi ressemble la génération d'un nombre aléatoire de 0 à 100 (inclus) :
import kotlin.random.Random\n\nfun main() {\n val randomNumber = Random.nextInt(0, 101) // de 0 à 100 inclus\n println("Nombre aléatoire : $randomNumber")\n}\n\nTout est simple. Mais regardons sous le capot. Random.nextInt(from, until) utilise en interne java.util.Random (sur la plateforme JVM). Et celui-ci, à son tour, génère des nombres à l'aide d'un générateur linéaire congruentiel (LCG). La formule est simple :
new_seed = (old_seed * A + C) mod M
Où A, C et M sont des constantes. C'est rapide, mais pas cryptographiquement sûr. Si un attaquant découvre ne serait-ce que quelques nombres, il peut reconstituer la seed et prédire tous les suivants.
\n\nQuand l'utiliser : Pour les jeux (pas d'argent), la sélection aléatoire d'un élément dans une liste, le mélange de cartes dans une application ordinaire.
\n\nNiveau avancé : ThreadLocalRandom pour le multithreading
\n\nSi votre application utilise activement les coroutines ou le multithreading, un Random ordinaire peut devenir un goulot d'étranglement. Chaque appel à nextInt() est synchronisé pour éviter les conditions de concurrence. La solution — 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\nCette classe alloue une instance distincte de Random pour chaque thread. Aucune synchronisation n'est nécessaire — chaque thread travaille avec son propre générateur. En pratique, cela offre un gain de performance dans les scénarios multithreads.
La sécurité avant tout : SecureRandom
\n\nImaginez maintenant que vous générez un mot de passe à usage unique (OTP) ou un jeton pour la récupération d'accès. Utiliser un Random ordinaire serait un crime. C'est là qu'intervient 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 collecte l'entropie à partir des sources du système d'exploitation : bruits du microphone, temporisations des frappes au clavier (dans l'émulateur — à partir du fichier /dev/random ou /dev/urandom). Cela le rend imprévisible. Mais cela se paie en vitesse — il peut être des dizaines de fois plus lent qu'un Random ordinaire.
Important : Sur Android, S