Код в MessageQueue
(который обрабатывает удаление сообщения): делает это :
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
// clearing code
}
где p
- сообщение в очереди, p.obj
- это связанный с ним токен, а object
- дополнительный токен, который вы передали для очистки сообщений. Таким образом, если вы передали токен , и он соответствует токену текущего сообщения, сообщение будет очищено.
Проблема в том, что для сравнения токенов используется ссылочное равенство - если они ' re не совсем тот же объект, если вы не передаете тот же экземпляр токена, с которым отправили сообщение, он не совпадает и ничего не происходит.
Когда вы объявляете token2
как 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)
. Вот что об этом говорится в документации :
Возвращает экземпляр Integer, представляющий указанное значение int. Если новый экземпляр Integer не требуется, этот метод обычно следует использовать вместо конструктора Integer (int) , так как этот метод, вероятно, даст значительно лучшую пространственную и временную производительность за счет кэширования часто запрашиваемых значений. Этот метод всегда будет кэшировать значения в диапазоне от -128 до 127 включительно и может кэшировать другие значения за пределами этого диапазона .
Так что вызов Integer(number)
будет всегда создает новый объект, valueOf(number)
может создать его, или может вернуть Integer
объект, созданный ранее. Значение 121 будет всегда возвращать тот же объект, что и раньше, поэтому вы получаете ссылочное равенство с этим объектом, поэтому токены совпадают. Для большего числа вы получаете разные объекты (вы можете проверить их идентификаторы в отладчике)
Но почему он работает в Java а не Kotlin? Я не тестировал с Java, но, возможно, кеш работает по-другому, возможно, компилятор сможет более разумно использовать один и тот же объект для переменной int
за пределами диапазона «определенно кэшированного». Или, если вы определяете свой токен в коде Java как Integer
вместо int
, тогда вы создаете один объект и передаете его оба раза, так что он всегда будет совпадать.
Во всяком случае, это большой опыт, чтобы попытаться помочь вам понять, почему он ломается! Краткая версия: не делайте этого, не позволяйте ему автоматически упаковывать вещи, создайте объект токена и сохраните ссылку на него, чтобы вы могли снова передать тот же экземпляр позже;)
(Это касается String
s тоже - Java имеет пул строк, в котором он повторно использует один и тот же объект, если вы дважды объявляете строковый литерал, но может не , поэтому безопаснее назначить String
переменной, и тогда вы знаете, что это всегда один и тот же объект)