2015-12-24 7 views
0

У меня есть проблема с моим собственным кодом JNI на Android 6 (ART). У меня есть метод, который получает экземпляр jclass и сохраняет эту ссылку класса для будущего использования. Чтобы сделать это, нужно создать ссылку на эту jclass так, чтобы он не стал недействительным позже:JNI: как проверить, что два экземпляра jclass действительно относятся к одному и тому же классу Java?

std::set<jclass> storedClasses; 

void storeClass(jclass cls) 
{ 
    TraceLog << "Original jclass:" << cls; 
    jclass clsRef = (jclass)env.jniEnv()->NewGlobalRef(cls); 
    TraceLog << "New jclass global reference:" << cls; 

    storedClasses.insert(clsRef); 
} 

Проблема заключается в том, что мне нужно хранить только каждый класс раз (отсюда и выбор std::set для контейнера - это гарантирует, что все элементы уникальны), но NewGlobalRef фактически создает новое, другое значение jclass каждый раз. Это имеет смысл, но это было не так до недавнего времени, я полагаю. Я больше не могу использовать это значение (это просто указатель), чтобы обеспечить уникальность.

Как проверить, указывают ли два разных экземпляра jclass на одни и те же или разные классы Java? Будет ли литье jclass обратно на jobject и при вызове java.lang.Object.hashcode на нем получить нужный результат?

Update: странно, я могу назвать hashcode() успешно, но всегда возвращается -1.
Обновление 2:this тоже не работает, все, что я получаю, это java.lang.Class независимо от того, как я стараюсь.

ответ

2

NewGlobalRef создает новую ссылку, а не новый объект или экземпляр объекта класса. Если вы выберете его дважды, вы получите два разных значения, ни один из которых не будет таким же, как и исходная локальная ссылка. Вызов IsSameObject скажет вам, что все они ссылаются на одно и то же, и является единственным надежным способом определить, указывают ли две ссылки на один и тот же объект.

Вы не можете тривиально создать набор со ссылками, потому что ссылки не уникальны. Если ваше единственное требование - не добавлять дублированный объект, вы можете сделать цикл O (n) из IsSameObject вызовов при добавлении нового члена. Используйте хэш-коды с осторожностью - хеш-код для объекта не изменится, поэтому, если хэш-коды не совпадают, у вас есть два разных объекта, но два разных объекта могут иметь один и тот же хэш-код.

Альтернативный подход заключается в использовании вызовов JNI для хранения объектов класса в структуре данных на Java-языке, такой как HashSet; это добавляет дополнительные накладные расходы для вызовов JNI, но если вы храните большое количество объектов, улучшенная структура данных может быть полезной.

(Trivia: pre-ICS, Dalvik просто вернет указатель на объект, на который ссылается, поэтому вы можете создать собственный набор тривиально. Такой подход сделал более сложным управление кучей, поэтому релиз ICS представил «косвенные» ссылки, которые являются добавленными индексами таблицы, а не указателями.)

+0

Спасибо, теперь я понимаю намного больше о JNI! И да, я помню массу ошибок JNI, которые произошли в моем приложении, когда вышла ICS. И тонна удовольствия, которую я их исправил :) –

+0

P. S. Не могли бы вы пояснить, как два разных объекта могут иметь один и тот же хеш-код? Я имею в виду, вы ссылаетесь на вероятностную природу хэшей и на то, что столкновение не является невозможным, или вы имеете в виду, что это действительно может произойти в реальном приложении со статистически значимой вероятностью? –

+0

Если хэш-код вычисляется (например, 'java.lang.String'), два * одинаковых * объекта всегда будут иметь одинаковый хеш. Для простого объекта хэш-код обычно устанавливается на 32-разрядный адрес объекта и «заперт» в первый раз, когда что-то его запрашивает (поэтому он остается неизменным, если объект перемещается). Это позволяет избежать явного сохранения хеш-кода в объектах, которые никогда не хранятся в хэш-контейнерах. В зависимости от характеристик GC может быть вероятным создание объектов в небольшом диапазоне адресов, что делает вероятными столкновения. Я не знаю, как работает ART. – fadden

0

Я думаю, что проблема заключается в том, что вы храните clsRef, что является jobject, которое вы передали в jclass. На самом деле это не jclass, а другой объект, который является ссылкой на ваш первоначальный класс.

Вам необходимо сохранить что-то, что действует как ключ для этого глобального ref в вашем магазине. Что-то должно однозначно идентифицировать класс.

Ваш магазин представляет собой набор, и я думаю, что вам нужно, например. хэш-таблицу.

Если FindClass() возвращает одинаковые значения для тех же параметров каждый раз, когда это может быть использовано, но я не могу проверить это на данный момент. Я подозреваю, что вам нужно сделать GetName(), но, опять же, не могу его переписать.

+0

Я попробовал 'GetName' (см. обновление 2 в моем Q) и возвращает' lang.Class' даже для исходного 'cls', который является фактическим оригинальным классом, который мой метод JNI. –

Смежные вопросы