Jni++ специально разработан для использования в проектах приложений для ОС Android.
Jni++ показывает, каким бы мог быть интерфейс JNI, если бы он был реализован с использованием стандарта C++11.
Отличительной особенностью Jni++ является перенос практически всего сервисного кода для JNI с плеч пользователя в область метапрограммирования и автоматической генерации кода.
Библиотека JNI спроектирован таким образом, что во время ее использования приходится мириться со следующими неудобствами:
JNINativeMethod
для правильной инициализации требует три параметра, второй (const char* signature
) из которых заставляет пользователя повторять объявление третьего (void* fnPtr
) в очень замысловатом синтаксисе.- Пользователь JNI обязан самостоятельно отслеживать время жизни окружения Java (инстанции
JNIEnv
) для каждого из своих низкоуровневых потоков. Этот факт неявно заставляет пользователей отказываться от низкоуровневой многопоточности в пользу высокоуровневой, что так же является сильным недостатком. - Преобразование простых типов между пространствами C++ и Java является тривиальной задачей, однако пользователей JNI обязали производить и это преобразование.
- Механизмы получения строковых значений со стороны Java, равно как и передача на сторону Java, выполнены в стиле C и не являются безопасными с точки зрения C++.
- Для получения идентификатора (статического или объектного) поля пользователь обязан продублировать тип нужного значения дважды, причем второй раз - в виде строки с очень замысловатым синтаксисом.
- Сигнатуру функции так же приходится дублировать в виде строки с очень замысловатым синтаксисом для получения ее идентификатора.
Очевидным является то, что многие дублирования в JNI распределены. Это представляет собой большое пространство для самых разных ошибок. Так же, при использовании JNI для решения задач, количество сервисного кода оказывается сильно больше количества функционального кода задачи. Это сильно затрудняет поиск ошибок в алгоритмах и делает практически невозможным решение задач даже среднего уровня.
В целом создается такое впечатление, будто JNI спроектирован так, чтобы косвенно заставлять разработчиков низкого уровня прибегать к решению своих задач на Java, а не с помощью JNI.
Jni++ создан для того, чтобы как можно больше рутины JNI с плеч пользователя переложить на метапрограммирование и этап компиляции. Стандарт языка C++ от 2011 года очень хорошо подходит для решения этой задачи.
Jni++ берет на себя следующие задачи:
- Слежение за инстанциями
JNIEnv
для всех низкоуровневых потоков пользователя. ИнстанцииJNIEnv
будут автоматически удалены прямо перед завершением работы потока. - Генерация строковых сигнатур на основании предоставленных типов. Jni++ выполняет генерацию сигнатур в время компиляции кода, что полностью удаляет связанные с этим задержки во время исполнения.
- Реализация в виде низкоуровневой рефлексии для Java кода с повсеместным применением ООП и RAII. Основными сущностями Jni++ являются классы, объекты, функции и поля из Java кода.
- Автоматическое преобразование типов между пространствами Java и C++ на основании предоставленных типов.
- Пользовательский код может быть полностью избавлен от типов и интерфейсов JNI. Jni++ позволяет оперировать только C++ типами и значениями.
- Возможность работы с Java кодом из любого потока. Jni++ потокобезопасен и предоставляет возможность конкурентного обращения к Java коду.
Jni::Class string_class{ "java/lang/String" };
Jni::Class
использует глобальные ссылки, давая возможность пользоваться результатом захвата в любом потоке.
jobject local_object; // reference from Java side.
Jni::Object captured_object{ local_object };
Jni::Object
так же использует глобальные ссылки, что позволяет удерживать Java-объект в рабочем состоянии.
Jni::Class string_class{ "java/lang/String" };
Jni::Object captured_object{ Jni::Object::NewObject( string_class ) };
// Load the class.
Jni::Class class_handle{ "java/lang/Class" };
// Capture the member-function from class.
Jni::MemberFunction<std::string> func_get_name{ class_handle, "getName" };
// Promote the class to object.
Jni::Object string_class{ Jni::Class{ "java/lang/String" } };
// Get the name of Java class. `java/lang/String` will be stored.
std::string string_class_name{ func_get_name.Call( string_class ) };
А теперь давай представим, сколько кода понадобилось бы написать с использованием JNI для реализации всего этого.
Библиотека Jni++ распространяется под лицензией Apache-2.0.
Библиотека не содержит код или библиотеки третьих лиц.
Корневой репозиторий проекта: https://github.com/FrankStain/jnipp