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

2023-11-30

我执行了这段代码Kotlinandroid 项目,它将记录这两条消息。如果我改变token to Char or 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处理程序不删除回调?

EDIT如果我尝试使用注释值,它会起作用


代码在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

至于为什么它与Int121,我假设它取决于 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然后您将创建一个对象并传递它两次,以便始终匹配。

无论如何,呃,这是很多背景知识,可以尝试帮助您找出它为什么会损坏!简而言之,不要这样做,不要让它自动装箱,创建一个令牌对象并保留对它的引用,以便稍后可以再次传递相同的实例;)

(这适用于StringJava 也有一个字符串池,如果你声明一个字符串文字两次,它会重用同一个对象,但它也许不会,所以分配一个更安全String到一个变量,然后你就知道它总是同一个对象)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐