Kotlin的随机数不随机
Kotlin的随机数不随机

大学,我们学校有3座餐厅,然后每个餐厅又有多层楼,可以说是条件还不错了。除了考前一周都无所事事的我们,每天面临最大的难题就是去哪儿吃,吃什么。

既然大家都是学软件的,那就写个app来解决这个问题吧。另外两个老哥( @ruiOVO@xyx )主要是写前端、服务端的,于是这个重任就交到了我身上。

于是大聪明我一拍脑门就写出来了,

// 实际上是写了个安卓app, 这里只是示意
val list = listOf("荟萃餐厅", "玉兰餐厅", "唐岛湾餐厅")
val floor = listOf("1楼", "2楼", "3楼")
println(list.random() + floor.random())

简洁优雅,不用算上下界,我们Kotlin真是太厉害辣!

然后我们就在荟萃餐厅一楼连续吃了3天……小伙伴们都不相信我的编程水平了。


故事就到这里,那么问题在哪里呢?

随机数不随机,那么肯定是初始化时没有引入不同seed。list.random()方法默认使用的Random对象定义在kotlin stdlib的Random.kt文件中

image-20221216183431623

接下来找到这个defaultPlatformRandom的实现

image-20221216202117167

这东西最终用的是PlatformThreadLocalRandom,而Android的ThreadLocalRandom中出了个bug——他在zygote进程中初始化,所以每个程序打开时都是相同的初始状态。

我们可以在网上找到相关的讨论:

https://www.reddit.com/r/androiddev/comments/x8z9r1/comment/innw882/?utm_source=share&utm_medium=web2x&context=3

https://youtrack.jetbrains.com/issue/KT-52618/ThreadLocalRandom-is-not-a-good-source-of-randomness-on-Android-before-SDK-34-so-dont-use-it-for-Kotlin-Random


解决:

  1. 避免使用List.random、Random.nextXxx之类的方法,他们最终都是调用这个PlatformThreadLocalRandom,每次需要随机数时都创建一个Random对象,传入seed:val random = Random(System.currentTimeMills());random.nextInt()
  2. 升级Kotlin版本,根据官方的缺陷跟踪器,该问题兜底修复于1.7.20版本
  3. 使用字节码修改的方法,编译时替换掉相关的调用,比如可以用lancet来处理

最后修改于 2022-12-16