Оболочка библиотеки для API-интерфейсов Android . Часть пакета разработки игр для Android .
Оболочка библиотеки — это инструмент командной строки (CLI), который генерирует код оболочки на языке C для API-интерфейсов Android, написанных на Java. Вы можете использовать этот код в собственных приложениях Android для вызова API Java без необходимости вручную создавать собственный интерфейс Java или JNI. Этот инструмент может упростить разработку приложений для Android, написанных в основном на C или C++.
Инструмент работает путем создания кода C для общедоступных символов, содержащихся в предоставленных вами файлах Java Archive (JAR), или классов, определенных в файле конфигурации инструмента, или того и другого. Код, созданный этим инструментом, не заменяет API Java; вместо этого он действует как мост между вашим кодом C и Java. Ваше приложение по-прежнему требует включения в проект библиотек Java, которые вы используете.
Скачать
Загрузите архив-оболочку библиотеки и распакуйте его содержимое в выбранный вами каталог.
Синтаксис
Инструмент-оболочка библиотеки имеет следующий синтаксис командной строки:
java -jar lw.jar \
[-i jar-file-to-be-wrapped] \
[-o output-path] \
[-c config-file] \
[-fa allow-list-file] \
[-fb block-list-file] \
[--skip_deprecated_symbols]
Параметр | Описание |
---|---|
-i jar-file-to-be-wrapped | JAR-файл для генерации кода оболочки C. Можно указать несколько JAR-файлов, например:-i first_library.jar -i second_library.jar... |
-o output-path | Местоположение файловой системы для сгенерированного кода. |
-c config-file | Путь файловой системы к файлу конфигурации оболочки библиотеки. Подробности смотрите в разделе Конфигурация . |
-fa allow-list-file | Путь к файлу фильтра, в котором можно указать символы для переноса инструмента. Подробности смотрите в разделе Фильтр . |
-fb block-list-file | Путь к файлу фильтра, содержащему символы, исключенные из переноса. Подробности смотрите в разделе Фильтр . |
--skip_deprecated_symbols | Указывает инструменту-оболочке пропускать символы @Deprecated . |
Конфигурационный файл оболочки
Конфигурация оболочки библиотеки представляет собой файл JSON, позволяющий управлять процессом генерации кода. В файле используется следующая структура.
{
// An array of type-specific configs. A type config is useful when a user wants to map
// a Java type to a manually defined C type without generating the code. For example, when a developer
// has their own implementation of the "java.lang.String" class, they can tell the generator to use it
// instead of generating it.
"type_configs": [
{
// [Required] Name of a fully qualified Java type.
"java_type": "java.lang.String",
// The C type that the java_type will be mapped to.
"map_to": "MyOwnStringImplementation",
// A header file that contains the declaration of the "map_to" type.
"source_of_definition": "my_wrappers/my_own_string_implementation.h",
// Controls if a value should be passed by pointer or value.
"pass_by_value": false
}
],
// An array of package-specific configs.
"package_configs": [
{
// [Required] A name of a Java package that this section regards. A wildchar * can be used at the
// end of the package name to apply this config to all packages whose name starts with this value.
"package_name": "androidx.core.app*",
// A subdirectory relative to the root directory where the generated code will be located.
"sub_directory": "androidx_generated/",
// If true, the generated file structure reflects the package name. For example, files generated
// for the package com.google.tools will be placed in the directory com/google/tools/.
"file_location_by_package_name": true,
// A prefix added to all class names from this package.
"code_prefix": "Gen",
// A prefix added to all generated file names from this package.
"file_prefix": = "gen_"
}
],
// An array of manually defined classes for wrapping. Defining classes manually is useful when a
// jar file with desired classes are not available or a user needs to wrap just a small part of an SDK.
"custom_classes": [
{
// [Required] A fully-qualified Java class name. To define inner class, use symbol "$", for example
// "class com.example.OuterClass$InnerClass".
"class_name": "class java.util.ArrayList<T>",
// List of methods.
"methods": [
"ArrayList()", // Example of a constructor.
"boolean add(T e)", // Example of a method that takes a generic parameter.
"T get(int index)", // Example of a method that returns a generic parameter.
"int size()" // Example of parameterless method.
]
},
]
}
Фильтровать файлы
Может оказаться полезным исключить некоторые символы из файлов JAR, которые вы планируете обернуть. Вы можете указать файл фильтра в своей конфигурации, чтобы исключить символы. Файл фильтра — это простой текстовый файл, в котором каждая строка определяет символ для переноса. Файлы фильтров используют следующий синтаксис:
java-symbol-name java-jni-type-signature
Ниже приведен пример файла фильтра:
# Class filter
java.util.ArrayList Ljava.util.ArrayList;
# Method filter
java.util.ArrayList.lastIndexOf (Ljava.lang.Object;)I
# Field filter
android.view.KeyEvent.KEYCODE_ENTER I
Вы предоставляете конфигурацию файл фильтра, определяющий символы, которые разрешены с помощью параметра -fa
, и заблокированные символы с помощью параметра -fb
. Оба параметра могут использоваться одновременно. Если предоставлены оба фильтра, символ будет перенесен, если он определен в файле разрешающего фильтра и отсутствует в файле блочного фильтра.
Пример сценария
Предположим, вам нужно обернуть JAR-файл ChatLibrary.jar
, содержащий следующий класс:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
Ваш проект C требует, чтобы вы создали собственную оболочку для этого JAR, позволяющую вашему родному приложению Android вызывать его во время выполнения. Сгенерируйте этот код, используя оболочку библиотеки, с помощью следующей команды:
java -jar lw.jar -i ChatLibrary.jar -o ./generated_code/
Предыдущая команда генерирует исходный код C в каталог ./generated_code
. Сгенерированный chat_manager.h
содержит код, подобный следующему, позволяющий вызывать библиотеку в вашем проекте:
#include "java/lang/string.h"
typedef struct ChatManager_ ChatManager;
void ChatManager_sendMessage(int32_t user_id, String* message);
Подробный пример сценария см. в руководстве по оболочке библиотеки .
Детали инструмента
В следующих разделах представлена подробная информация о функциях оболочки библиотеки.
Структура выходного каталога
Все исходные файлы C и файлы заголовков расположены в подкаталогах, которые отражают имя пакета обернутого класса Java. Например, код-оболочка для указанного JAR java.lang.Integer
создается в каталоге ./java/lang/integer.[h/cc]
.
Вы можете управлять этим поведением вывода, используя файл конфигурации инструмента.
Жизненный цикл объекта
Объекты Java представлены в коде C как непрозрачные указатели, называемые оболочками. Оболочка управляет ссылкой JNI для соответствующего объекта Java. Обертку можно создать в следующих сценариях:
- Обертывая существующую ссылку JNI, вызывая функцию
MyClass _wrapJniReference(jobject jobj)
. Функция не становится владельцем предоставленной ссылки, а создает собственную глобальную ссылку JNI. - Путем создания нового объекта, что эквивалентно вызову конструктора в Java:
MyClass _construct()
- Возвращая новую оболочку из функции, например:
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
Вам необходимо уничтожить все обертки, когда они больше не используются. Для этого вызовите специальную функцию destroy()
MyClass _destroy( MyClass * instance)
.
Функции, возвращающие оболочки, выделяют для них новую память для каждого вызова, даже если оболочки представляют один и тот же экземпляр Java.
Например, когда метод Java Singleton.getInstance()
всегда возвращает один и тот же экземпляр, эквивалентная функция на стороне C создаст новый экземпляр оболочки для того же экземпляра Java:
Singleton* singleton_a = Singleton_getInsance();
Singleton* singleton_b = Singleton_getInsance();
// singleton_a and singleton_b are different pointers, even though they represent the same Java instance.
Обрабатывать классы, на которые нет ссылок
Если класс не удается найти в предоставленном JAR-файле, библиотека-оболочка создает базовую реализацию, состоящую из непрозрачного указателя и следующих методов:
-
wrapJniReference()
-
getJniReference()
-
destroy()
Детали генерации кода
При запуске оболочка библиотеки генерирует код C на основе общедоступных символов в файлах JAR, которые вы предоставляете инструменту. Сгенерированный код C может отличаться от завернутого кода Java. Например, C не поддерживает такие функции, как ООП, универсальные типы, перегрузку методов и другие функции Java.
Сгенерированный код C, отражающий эти ситуации, может отличаться от типа кода, ожидаемого разработчиками C. Примеры в следующих разделах предоставляют контекст того, как инструмент может генерировать C из кода Java. Примечание. Следующие примеры в фрагментах кода включают фрагменты кода C/C++ и Java. Эти фрагменты предназначены исключительно для демонстрации того, как инструмент генерирует код для каждой конкретной ситуации.
Классы
Классы представлены в C как непрозрачные указатели :
С/С++
typedef struct MyClass_ MyClass;
Ява
public class MyClass { ... }
Экземпляры непрозрачных указателей называются оболочками . Инструмент-оболочка генерирует дополнительные функции поддержки для каждого класса. Для предыдущего примера класса MyClass
генерируются следующие функции:
// Wraps a JNI reference with MyClass. The 'jobj' must represent MyClass on the Java side.
MyClass* MyClass_wrapJniReference(jobject jobj);
// Return JNI reference associated with the 'MyClass' pointer.
jobject MyClass_getJniReference(const MyClass* object);
// Destroys the object and releases underlying JNI reference.
void MyClass_destroy(const MyClass* object);
Конструкторы
Классы с открытыми конструкторами или конструкторами по умолчанию представляются с помощью специальных функций:
С/С++
MyClass* MyClass_construct(String* data);
Ява
public class MyClass {
public MyClass(String data) { ... }
}
Методы
Методы представлены как обычные функции. Имя функции содержит исходное имя класса. Функции, представляющие нестатические методы экземпляра, имеют в качестве первого параметра указатель на структуру, представляющую объект Java, от имени которого вызывается функция. Этот подход аналогичен указателю this
.
С/С++
Result* MyClass_doAction(const MyClass* my_class_instance, int32_t action_id, String* data);
int32_t MyClass_doAction(int32_t a, int32_t b);
Ява
public class MyClass {
public Result doAction(int actionId, String data) { ... }
public static int doCalculations(int a, int b) { ... }
}
Внутренние классы
Внутренние классы представлены близко к обычным классам, за исключением того, что имя соответствующей структуры C содержит связанные имена внешних классов:
С/С++
typedef struct MyClass_InnerClass_ MyClass_InnerClass;
Ява
public class MyClass {
public class InnerClass {...}
}
Внутренние методы класса
Внутренние методы класса представлены следующим образом:
С/С++
bool MyClass_InnerClass_setValue(MyClass_InnerClass* my_class_inner_class_instance, int32_t value);
Ява
public class MyClass {
public class InnerClass {
public boolean setValue(int value) { ... }
}
}
Общие типы
Оболочка библиотеки не охватывает универсальные типы напрямую. Вместо этого инструмент создает оболочки только для экземпляров универсального типа.
Например, если класс MyGeneric<T>
существует в API и существует два экземпляра этого класса, например MyGeneric<Integer>
и MyGeneric<String>
, создаются оболочки для этих двух экземпляров. Это означает, что вы не можете создавать новые экземпляры типа MyGeneric<T>
используя разные конфигурации типов. См. следующий пример:
С/С++
// result.h
typedef struct Result_Integer_ Result_Integer;
typedef struct Result_Float_ Result_Float;
Integer* Result_Integer_getResult(const Result_Integer* instance);
Float* Result_Float_getResult(const Result_Float* instance);
// data_processor.h
typedef struct DataProcessor_ DataProcessor;
Result_Integer* DataProcessor_processIntegerData(const DataProcessor* instance);
Result_Float* DataProcessor_processFloatData(constDataProcessor* instance);
Ява
public class Result<T> {
public T getResult();
}
public class DataProcessor {
public Result<Integer> processIntegerData();
public Result<Float> processFloatData();
}
Реализация интерфейсов
Реализуйте интерфейс C, вызвав implementInterface()
и предоставив функцию обратного вызова для каждого метода интерфейса. Таким образом могут быть реализованы только интерфейсы; классы и абстрактные классы не поддерживаются. См. следующий пример:
С/С++
// observer.h
typedef struct Observer_ Observer;
typedef void (*Observer_onAction1Callback)();
typedef void (*Observer_onAction2Callback)(int32_t data);
Observer* Observer_implementInterface(
Observer_onAction1Callback observer_on_action1_callback,
Observer_onAction2Callback observer_on_action2_callback);
Ява
public interface Observer {
void onAction1();
void onAction2(int data);
}
public class Subject {
public void registerObserver(Observer observer);
}
Пример использования:
void onAction1() {
// Handle action 1
}
void onAction2(int32_t data) {
// Handle action 2
}
Observer* observer = Observer_implementInterface(onAction1, onAction2);
Subject_registerObserver(subject, observer);
Ограничения
Инструмент-оболочка библиотеки находится в стадии бета-тестирования. Вы можете столкнуться со следующими ограничениями:
Неподдерживаемые конструкции Java
Бета-версия оболочки библиотеки не поддерживает следующие конструкции:
Перегрузка метода
Язык C не позволяет объявлять две функции с одинаковым именем. Если класс использует перегрузку методов, сгенерированный код C не будет компилироваться. Обходной путь — использовать только один метод с достаточным набором параметров. Остальные функции можно отфильтровать с помощью фильтров . Это касается и конструкторов.
Шаблонные методы
Поля, отличные от
static final int
иstatic final String
Массивы
Возможные конфликты имен
Из-за того, как классы Java представлены в коде C, в очень редких случаях могут возникать конфликты имен. Например, класс Foo<Bar>
и внутренний класс Bar
внутри класса Foo
представлены одним и тем же символом в C: typedef struct Foo_Bar_ Foo_Bar;
Поддерживать
Если вы обнаружите проблему с оболочкой библиотеки, сообщите нам об этом.
Просмотр ошибок | Сообщить об ошибке |
---|---|
Инженерное дело | bug_report |
Документация | bug_report |