令牌类型为Int或Long(* Kotlin)的Android处理程序回调未删除 [英] Android Handler callback not removed for token type Int or Long (*Kotlin)

查看:75
本文介绍了令牌类型为Int或Long(* Kotlin)的Android处理程序回调未删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Kotlin android项目中执行了此代码,它将记录两条消息.如果将token更改为CharString,它将仅打印一条消息,该消息是所需的行为. android中的Java项目中的相同用例可以正常工作.

I have this code executed in Kotlin android project and it will log both messages. If I change the token to Char or String it will print only one message which is the wanted behaviour. Same use-case in a java project in android works as it should.

    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中对于类型为IntLong的令牌,处理程序不会删除回调?

My question is why in Kotlin for a token of type Int, Long the handler doesnt remove the callback?

编辑 如果我尝试使用带注释的值,则可以使用

EDIT If I try with commented values it works

推荐答案

MessageQueue中的代码(用于处理消息删除)为

The code in MessageQueue (which is handling the message deletion) is doing this:

while (p != null && p.target == h
                    && (object == null || p.obj == object)) {

// clearing code
}

其中,p是队列中的消息,p.obj是与其关联的令牌,而object是为清除消息而传递的可选令牌.因此,如果您传递了令牌,并且该令牌与当前消息的令牌匹配,则该消息将被清除.

where p is a message in the queue, p.obj is the token associated with it, and object is the optional token you've passed in to clear messages for. So if you have passed in a token, and it matches the token of the current message, the message gets cleared.

问题在于它使用引用相等性来比较令牌-如果它们不是完全相同的对象,如果您不传递与您发布消息的对象相同的令牌实例,则该令牌不匹配并且什么也没有发生.

The problem is it uses referential equality to compare tokens - if they're not exactly the same object, if you're not passing in the same token instance you posted the message with, it doesn't match and nothing happens.

当您将token2声明为Kotlin自己的某种原语"作为Int,然后将其传递到需要实际对象的方法中时,它将装箱到Integer中.然后您执行两次-一次发布带有令牌的消息,一次清除带有令牌的消息.每次都会创建一个不同的(非参照相等)对象.

When you declare token2 as an Int, which is Kotlin's own "kind of a primitive", and then pass it into a method that requires an actual object, it gets boxed into an Integer. And you do that twice - once to post a message with a token, once to clear messages with a token. It creates a different (non-referentially equal) object each time.

您可以通过存储令牌对象并进行比较来对此进行测试:

You can test this by storing the token objects and comparing them:

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). 文档中对此的评论:

As for why it works with an Int of 121, I'm assuming it's down to Java's integer cache. Under the hood, the Kotlin code (if you do Show Bytecode and then decompile it) is calling Integer.valueOf(token2). Here's what the docs say about it:

返回一个表示指定int值的Integer实例. 如果不需要新的Integer实例,通常应优先于构造方法Integer(int)使用此方法,因为此方法通过缓存经常请求的值可能会产生明显更好的空间和时间性能. 此方法将始终缓存-128至127(包括)范围内的值,并且可能会缓存该范围之外的其他值..

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

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

So calling Integer(number) will always create a new object, valueOf(number) might create one, or it might return an Integer object it created earlier. A value of 121 will always return the same object as before, which is why you're getting referential equality with that one, so the tokens match. For the larger number, you're getting different objects (you can check their IDs in the debugger)

但是为什么它在Java中而不是Kotlin中起作用?我没有使用Java进行测试,但是缓存的工作方式可能有所不同,也许编译器可以更聪明地将相同对象用于绝对缓存"对象之外的int变量.范围.或者,如果您在Java代码中将令牌定义为Integer而不是int,那么您将创建一个对象并将其两次传递,这样始终会匹配.

But why does it work in Java and not Kotlin? I didn't test with Java, but it's possible the cache is working differently, maybe the compiler is able to be smarter about reusing the same object for an int variable outside of the "definitely cached" range. Or if you're defining your token in your Java code as an Integer instead of an int then you're creating one object and passing it both times, so that will always match.

无论如何,有很多背景可以尝试并帮助您弄清为什么它破了!简短的版本是不要这样做,不要让它自动装箱东西,创建令牌对象并保留对其的引用,以便您以后可以再次传递相同的实例;)

Anyhow uh that's a lot of background to try and help you work out why it's breaking! The short version is don't do that, don't let it autobox things, create a token object and keep a reference to it so you can pass the same instance again later ;)

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

(This goes for Strings too - Java has a String pool where it reuses the same object if you declare a string literal twice, but it might not, so it's safer to assign a String to a variable, and then you know it's always the same object)

这篇关于令牌类型为Int或Long(* Kotlin)的Android处理程序回调未删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆