未删除令牌类型Int或Long(*Kotlin)的Android处理程序回调

未删除令牌类型Int或Long(*Kotlin)的Android处理程序回调,android,kotlin,android-handler,Android,Kotlin,Android Handler,我在Kotlinandroid项目中执行了这段代码,它将记录这两条消息。如果我将token更改为Char或String,它将只打印一条所需行为的消息。在android中的java项目中,同样的用例可以正常工作 val handler = Handler() //val token1: Long = 1001L //val token2: Int = 121 val token1: Long = 1001L val token2: Int = 1002

我在
Kotlin
android项目中执行了这段代码,它将记录这两条消息。如果我将
token
更改为
Char
String
,它将只打印一条所需行为的消息。在android中的java项目中,同样的用例可以正常工作

    val handler = Handler()
    //val token1: Long = 1001L
    //val token2: Int = 121
    val token1: Long = 1001L
    val token2: Int = 1002

    handler.postAtTime(
        {
            Log.e("postAtTime 1", " printed 1 ")
            handler.removeCallbacksAndMessages(token2)
        },
        token1,
        SystemClock.uptimeMillis() + 2000
    )

    handler.postAtTime(
        {
            Log.e("postAtTime 2", " printed 2 ")
        },
        token2,
        SystemClock.uptimeMillis() + 4000
    )
我的问题是为什么在Kotlin中,对于类型为
Int
Long
的标记,处理程序不删除回调

编辑
如果我尝试使用注释值,它会起作用。

消息队列中的代码(处理消息删除)是:

其中,
p
是队列中的消息,
p.obj
是与其关联的令牌,
object
是您传入以清除消息的可选令牌。因此,如果您传入了一个令牌,并且它与当前消息的令牌相匹配,那么消息将被清除

问题是它使用引用相等来比较令牌-如果它们不是完全相同的对象,如果您没有传递发布消息时使用的同一令牌实例,则不匹配,并且不会发生任何事情


当您将
token2
声明为
Int
,这是Kotlin自己的“一种原语”,然后将其传递到需要实际对象的方法中时,它会被装箱为
整数。您可以这样做两次—一次是用令牌发布消息,一次是用令牌清除消息。它每次都创建一个不同的(非引用相等的)对象

您可以通过存储令牌对象并比较它们来测试这一点:

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
)
至于为什么它使用的
Int
为121,我假设这取决于Java的整数缓存。在引擎盖下,Kotlin代码(如果您执行
显示字节码,然后对其进行反编译)正在调用
Integer.valueOf(token2)

返回表示指定int值的整数实例如果不需要新的整数实例,通常应优先使用此方法而不是构造函数Integer(int),因为此方法可能通过缓存频繁请求的值来产生显著更好的空间和时间性能此方法将始终缓存-128到127(含)范围内的值,并可能缓存此范围以外的其他值

因此,调用
Integer(number)
始终创建一个新对象,
valueOf(number)
可能会创建一个对象,或者可能会返回它先前创建的
Integer
对象。值为121将始终返回与以前相同的对象,这就是为什么您将获得与该对象的引用相等,因此标记匹配。对于较大的数目,您将获得不同的对象(您可以在调试器中检查它们的ID)


但为什么它在Java中工作而不是在Kotlin中?我没有使用Java进行测试,但缓存的工作方式可能不同,也许编译器能够更聪明地为“绝对缓存”范围之外的
int
变量重用相同的对象。或者,如果您在Java代码中将令牌定义为
整数
而不是
整数
,那么您将创建一个对象并同时传递它,以便始终匹配

不管怎样,嗯,这是很多背景知识,可以帮助你找出它为什么会破裂!简短的版本是:不要这样做,不要让它自动装箱,创建一个令牌对象并保留对它的引用,以便以后可以再次传递相同的实例;)


(这也适用于
String
s-Java有一个字符串池,如果您两次声明字符串文字,它会重用同一个对象,但可能不会,因此将
String
分配给变量更安全,这样您就知道它始终是同一个对象)

我尝试了这段代码,它只打印一个可能与我的机器相关的对象,但我和其他同事一起尝试过,他们也遇到了同样的问题。我的来宾,Long和Int在Kotlin中是primative类型,在JVM代码中,这意味着当您使用Object调用removeCallbacksAndMessages时,它将自动装箱,并在Int和Long的情况下创建新对象。在Kotlin中,我建议您使用String还是Long?智力?它是对象类型。
char
,在java中,它的基本权利是什么,但这里的行为与
int
不同?它也为我打印了一次。谢谢你的回答,我尝试了
int
,因为这个答案,但我错了是的,这是整个自动装箱的事情,可能有效,也可能无效,这取决于JVM是否喜欢您;)当您需要某种类型的标记时,最好的做法是始终自己创建一个对象(无论您喜欢什么类型,只要它是
对象
整数
而不是原语),然后按住它,以便稍后可以再次传递完全相同的对象。关键是对象本身,而不是内容,所以你需要抓住它!
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