Обновить

Комментарии 10

Не очень понятно. Чем такой подход лучше общепринятого.
На стороне java:


static HashMap<Long, Listener> listeners = new HashMap<>();

static void nativeEvent(long handle, String msg) {
Listener listener = listeners.get(handle);
if (listener == null){
return;
}
listener.print(msg);
}

Не нужно в нативе хранить ссылки на listener. Static функция всегда имеет один и тот же jmethodID, так что можно кэшировать и использовать везде.
Нативу вообще ничего не нужно знать о всяких листенерах и живы они или нет.

У меня в нэйтиве много классов с различными листенерами, с разным набором параметров и возвращаемых значений. В вашем примере придется делать отдельный NativeEvent для каждой функции (которых даже в одном отдельном листенере может быть больше одной). Да и самих листенеров может быть много - какие-то из них необязательные и тд. В общем, если листенер один с одной единственной функцией - ваше решение, пожалуй, и правда будет рациональнее. Если нативных классов много и у каждого свой листенер со своим набором функций - моя реализация кажется мне удобнее.

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

Спасибо за вопрос, возможно позже дополню статью пояснениями.

Так как в статье упоминается android то "общепринятым" подразумевается подход google.
Если посмотреть исходники фреймворка то там так и есть.
static методы в которых первым идет int с типом коллбека и обычно куча еще параметров, который из натива чаще всего приходят как null.
Ну и потом портянки switch-case :)

Не хочу оспаривать методы Google, но для собственных проектов мой вариант кажется мне красивее и удобнее, чем "портянки switch-case" и функция с кучей параметров. С точки зрения масштабирования - тоже. В проект могут добавляться новые классы с новыми листенерами, всякий раз менять кол-во параметров static функции и переписывать соответствующие участки кода? Не, я слишком ленив, да простит меня Гугл

Опять же, возвращаемое значение... Возвращать как Object, а потом плясать с бубном ради получения простого типа? Зачем, если большинство функций возвращают int, а одна-две - скажем, коллекцию или строку... в них как раз можно и заморочиться, а большую часть кода оставить простой и чистой

Запоздало вспомнил еще одно преимущество моего метода: легкость переноса на другие системы. К примеру, С++ связывается со Swift без плясок с JNI, но общая структура кода остается похожей. Я просто беру, немного правлю нативный код, немного - код оболочки (да, перевод на другой язык, но логика сохраняется полностью), и вуаля - у меня уже аналогичные обертки под IOS. С протестированной логикой, что тоже весомый фактор по сравнению с тем, чтобы на каждой системе городить собственный огород и затем заново тестировать.

Чем ловить баги отдельно для каждой системы, лучше сделать код общим, насколько это возможно, такой у меня подход.

Подход интересный, но на десктопе, наверное, уже проще Panama FFM использовать (Java 17+). Там можно передавать java-функции, как ссылки для upcall из нативного кода через MethodHandle. Классический пример с qsort с компаратором реализованный на java.

За десктоп не скажу, этот подход для Android использую, где UI на java - не выбор, а данность. Для десктопных версий своих приложений пишу UI на C++, под gtk4.

Может имеет смысл рассмотреть переход на flutter.
Там и биндинги к нативу присать проще и GUI для всех систем на одном языке получится.

Беда в том что FFM пока нет в Andorid. В бета android 17 подтянули до java25, но на приемлемый процент пользователей это раскатится лет через 5 минимум.
А делать две обертки на одном и том же языке совсем не разумно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации