代码在MessageQueue
(正在处理消息删除)是做这个:
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
// clearing code
}
where p
是队列中的消息,p.obj
是与其关联的令牌,并且object
是您传入以清除消息的可选令牌。所以如果你have传入一个令牌,并且它与当前消息的令牌匹配,该消息将被清除。
问题是它使用引用相等来比较令牌 - 如果它们不是完全相同的对象,如果您没有传入发布消息的同一令牌实例,则它不匹配并且什么也不会发生。
当你声明token2
as an Int
,这是 Kotlin 自己的“一种原语”,然后将其传递给需要实际对象的方法,它会被装箱到一个Integer
。您会执行两次 - 一次是使用令牌发布消息,一次是使用令牌清除消息。它每次都会创建一个不同的(非引用相等)对象。
您可以通过存储令牌对象并比较它们来测试这一点:
val handler = Handler()
//val token1: Long = 1001L
//val token2: Int = 121
val token1: Long = 1001L
val token2: Int = 1002
var postedToken: Any? = null
var cancelledToken: Any? = null
fun postIt(r: ()->Unit, token: Any, time: Long): Any {
handler.postAtTime(r, token, time)
return token
}
fun cancelIt(token: Any): Any {
handler.removeCallbacksAndMessages(token)
return token
}
postIt(
{
Log.e("postAtTime 1", " printed 1 ")
cancelledToken = cancelIt(token2)
// referential equality, triple-equals!
Log.e("Comparing", "Posted === cancelled: ${postedToken === cancelledToken}")
},
token1,
SystemClock.uptimeMillis() + 2000
)
postedToken = postIt(
{
Log.e("postAtTime 2", " printed 2 ")
},
token2,
SystemClock.uptimeMillis() + 4000
)
E/Comparing: Posted === cancelled: false
至于为什么它与Int
121,我假设它取决于 Java 的整数缓存。在幕后,Kotlin 代码(如果你这样做Show Bytecode
然后反编译它)正在调用Integer.valueOf(token2)
. 这是文档对此的描述:
返回表示指定 int 值的 Integer 实例。如果不需要新的 Integer 实例,通常应优先使用此方法而不是构造函数 Integer(int),因为此方法可能通过缓存频繁请求的值来产生显着更好的空间和时间性能。此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值.
所以打电话Integer(number)
will always创建一个新对象,valueOf(number)
might创建一个,或者它might返回一个Integer
它之前创建的对象。值为 121 将always返回与之前相同的对象,这就是为什么您获得与该对象的引用相等的原因,因此标记匹配。对于较大的数字,您将获得不同的对象(您可以在调试器中检查它们的 ID)
但为什么它可以在 Java 中运行,而不能在 Kotlin 中运行呢?我没有使用 Java 进行测试,但缓存的工作方式可能不同,也许编译器能够更智能地重用同一对象int
变量超出“明确缓存”范围。或者,如果您在 Java 代码中将令牌定义为Integer
而不是int
然后您将创建一个对象并传递它两次,以便始终匹配。
无论如何,呃,这是很多背景知识,可以尝试帮助您找出它为什么会损坏!简而言之,不要这样做,不要让它自动装箱,创建一个令牌对象并保留对它的引用,以便稍后可以再次传递相同的实例;)
(这适用于String
Java 也有一个字符串池,如果你声明一个字符串文字两次,它会重用同一个对象,但它也许不会,所以分配一个更安全String
到一个变量,然后你就知道它总是同一个对象)