Bibliotheks-Wrapper für Android APIs Teil des Android Game Development Kit.
Der Bibliotheks-Wrapper ist ein Befehlszeilentool, das die C-Sprache Wrapper-Code für Android-APIs, die in Java geschrieben sind. Sie können diese in nativen Android-Apps so Code erstellen, dass Java-APIs aufgerufen werden können, ohne eine Java Native Interface oder JNI erstellen. Dieses Tool kann die Entwicklung vereinfachen, Android-Apps, die hauptsächlich in C oder C++ geschrieben sind
Das Tool generiert C-Code für die öffentlichen Symbole, die in JAR-Dateien (Java Archive) oder Klassen, die im Konfigurationsdatei oder beides. Java wird nicht durch Code ersetzt, der vom Tool generiert wird. APIs stattdessen als Brücke zwischen Ihrem C-Code und Java. Standbild der App setzt voraus, dass die von Ihnen verpackten Java-Bibliotheken in Ihr Projekt aufgenommen werden.
Herunterladen
Laden Sie das Bibliotheks-Wrapper-Archiv herunter und entpacken Sie den Inhalt in das Verzeichnis. Ihrer Wahl.
Syntax
Das Bibliotheks-Wrapper-Tool hat die folgende Befehlszeilensyntax:
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]
Parameter | Beschreibung |
---|---|
-i jar-file-to-be-wrapped |
JAR-Datei, um C-Wrapper-Code zu generieren. Es können mehrere JARs angegeben werden,
Beispiel: -i first_library.jar -i second_library.jar...
|
-o output-path |
Speicherort des Dateisystems für den generierten Code. |
-c config-file |
Dateisystempfad zur Konfigurationsdatei des Bibliotheks-Wrappers. Weitere Informationen finden Sie im Abschnitt Konfiguration. |
-fa allow-list-file |
Ein Pfad zu einer Filterdatei, in der Sie Symbole angeben können, die das Tool verwenden soll. zusammenfassen. Weitere Informationen finden Sie im Abschnitt Filter. |
-fb block-list-file |
Ein Pfad zu einer Filterdatei mit Symbolen, die vom Wrapping ausgeschlossen sind. Für finden Sie im Abschnitt Filter weitere Informationen. |
--skip_deprecated_symbols |
Weist das Wrapper-Tool an, zu überspringen @Eingestellt . |
Wrapper-Konfigurationsdatei
Die Konfiguration des Bibliotheks-Wrappers ist eine JSON-Datei, mit der Sie die bei der Codegenerierung. Die Datei hat die folgende Struktur.
{
// 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.
]
},
]
}
Dateien filtern
Es kann nützlich sein, einige Symbole aus den JAR-Dateien auszuschließen, die Sie verpacken möchten. Ich können Sie in Ihrer Konfiguration eine Filterdatei angeben, um Symbole auszuschließen. Eine Filterdatei ist eine einfache Textdatei, in der jede Zeile ein umgebrochenes Symbol definiert. Filterdateien verwenden folgende Syntax:
java-symbol-name java-jni-type-signature
Hier sehen Sie ein Beispiel für eine Filterdatei:
# 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
Sie stellen für die Konfiguration eine Filterdatei mit den Symbolen bereit, die
sind mit dem -fa
-Parameter zulässig und blockierte Symbole mithilfe der -fb
. Beide Parameter können gleichzeitig verwendet werden. Wenn beide Filter
angegeben, wird das Symbol umschlossen, wenn es in der Datei zum Zulassen von Filtern definiert ist.
und nicht in der Blockfilterdatei vorhanden.
Beispielszenario
Angenommen, Sie müssen die JAR-Datei ChatLibrary.jar
mit dem
folgende Klasse:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
Ihr C-Projekt erfordert, dass Sie einen nativen Wrapper für diese JAR-Datei generieren, in Ihre native Android-App ein, um sie während der Laufzeit aufzurufen. Generieren Sie diesen Code mithilfe der Bibliotheks-Wrapper mit dem folgenden Befehl ein:
java -jar lw.jar -i ChatLibrary.jar -o ./generated_code/
Der vorherige Befehl generiert C-Quellcode für das Verzeichnis
./generated_code
Die generierte Datei chat_manager.h
enthält Code ähnlich dem folgenden:
So können Sie die Bibliothek in Ihrem Projekt aufrufen:
#include "java/lang/string.h"
typedef struct ChatManager_ ChatManager;
void ChatManager_sendMessage(int32_t user_id, String* message);
Ein detailliertes Beispielszenario finden Sie im Leitfaden zu Bibliotheks-Wrappern.
Tool details
In den folgenden Abschnitten finden Sie ausführliche Informationen Funktionalität.
Struktur des Ausgabeverzeichnisses
Alle C-Quell- und Header-Dateien befinden sich in Unterverzeichnissen, die den
Paketname der umschlossenen Java-Klasse. Ein Beispiel: Der Wrapper-Code für den
die angegebene JAR-java.lang.Integer
-Datei im Verzeichnis generiert wird
./java/lang/integer.[h/cc]
.
Sie können dieses Ausgabeverhalten mithilfe der Konfigurationsdatei.
Objektlebenszyklus
Java-Objekte werden im C-Code als intransparente Zeiger dargestellt, die als Wrapper bezeichnet werden. Ein Wrapper verwaltet eine JNI-Referenz für ein entsprechendes Java-Objekt. Ein Wrapper kann in folgenden Fällen erstellt werden:
- Durch Aufrufen der Funktion eine vorhandene JNI-Referenz zusammenfassen
MyClass_wrapJniReference(jobject jobj)
Die Funktion übernimmt nicht die Eigentumsrechte an der angegebenen Referenz, sondern erstellt eine eigene globale JNI-Referenz. - Durch Erstellen eines neuen Objekts, was dem Aufrufen eines Konstruktors in
Java:
MyClass_construct()
- Durch die Rückgabe eines neuen Wrappers von einer Funktion. Beispiel:
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
Sie müssen alle Wrapper löschen, wenn sie nicht mehr verwendet werden. Rufen Sie dazu die spezielle
destroy()
-Funktion MyClass_destroy(MyClass* instance)
.
Funktionen, die Wrapper zurückgeben, weisen bei jedem Aufruf einen neuen Arbeitsspeicher zu. auch wenn Wrapper für dieselbe Java-Instanz stehen.
Wenn beispielsweise die Java-Methode Singleton.getInstance()
immer den Fehlercode
in derselben Instanz erstellt die entsprechende Funktion auf der C-Seite eine neue Instanz,
eines Wrappers für dieselbe Java-Instanz:
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.
Umgang mit nicht referenzierten Klassen
Wenn eine Klasse in einer bereitgestellten JAR-Datei nicht gefunden werden kann, erstellt der Libarary-Wrapper eine grundlegende Implementierung, die aus einem opaken Zeiger und den folgenden Methoden besteht:
wrapJniReference()
getJniReference()
destroy()
Details zur Codegenerierung
Bei der Ausführung generiert der Bibliotheks-Wrapper C-Code basierend auf den öffentlichen Symbolen in die JAR-Dateien, die Sie dem Tool zur Verfügung stellen. Der generierte C-Code kann Abweichungen aufweisen aus dem umschlossenen Java-Code. Zum Beispiel unterstützt C keine Funktionen wie OOP, generische Typen, Methodenüberlastung oder andere Java-Funktionen.
Der generierte C-Code für diese Situationen kann vom Typ von C-Entwickelnden erwartet wird. Die Beispiele in den folgenden Abschnitten wie das Tool C aus Java-Code generieren kann. Hinweis: In den Code-Snippets enthalten die folgenden Beispiele C/C++- und Java-Code. Snippets. Diese Snippets sollen lediglich zeigen, wie das Tool der Code für die jeweilige Situation generiert.
Klassen
Klassen werden in C als undurchsichtige Zeiger dargestellt:
C/C++
typedef struct MyClass_ MyClass;
Java
public class MyClass { ... }
Instanzen von undurchsichtigen Zeigern werden als Wrapper bezeichnet. Das Wrapper-Tool
zusätzliche Supportfunktionen für jede Klasse generiert. Für das vorherige Beispiel
Klasse MyClass
, werden die folgenden Funktionen generiert:
// 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);
Konstruktoren
Klassen mit öffentlichen oder Standardkonstruktoren werden mithilfe spezieller Funktionen:
C/C++
MyClass* MyClass_construct(String* data);
Java
public class MyClass {
public MyClass(String data) { ... }
}
Methoden
Methoden werden als normale Funktionen dargestellt. Der Name einer Funktion enthält
ursprünglichen Kursnamen. Funktionen, die nicht statische Instanzmethoden darstellen, haben
als ersten Parameter einen Zeiger auf eine Struktur, die ein Java-Objekt darstellt,
für den die Funktion aufgerufen wird. Dieser Ansatz ist analog zu dem this
Zeiger.
C/C++
Result* MyClass_doAction(const MyClass* my_class_instance, int32_t action_id, String* data);
int32_t MyClass_doAction(int32_t a, int32_t b);
Java
public class MyClass {
public Result doAction(int actionId, String data) { ... }
public static int doCalculations(int a, int b) { ... }
}
Innere Klassen
Innere Klassen werden ähnlich wie normale Klassen dargestellt, mit Ausnahme des Namens der entsprechende C-Struktur die verketteten Namen der äußeren Klassen enthält:
C/C++
typedef struct MyClass_InnerClass_ MyClass_InnerClass;
Java
public class MyClass {
public class InnerClass {...}
}
Methoden der inneren Klasse
Methoden für innere Klassen werden so dargestellt:
C/C++
bool MyClass_InnerClass_setValue(MyClass_InnerClass* my_class_inner_class_instance, int32_t value);
Java
public class MyClass {
public class InnerClass {
public boolean setValue(int value) { ... }
}
}
Allgemeine Typen
Der Bibliotheks-Wrapper umschließt generische Typen nicht direkt um. Stattdessen wird das Tool generiert nur Wrapper für generische Instanziierungen.
Beispiel: Eine Klasse MyGeneric<T>
ist in einer API vorhanden und es gibt zwei
Instanziierungen dieser Klasse, z. B. MyGeneric<Integer>
und
MyGeneric<String>
haben, werden Wrapper für diese beiden Instanziierungen generiert. Dieses
Sie können also keine neuen Instanziierungen vom Typ MyGeneric<T>
mit
mit unterschiedlichen Typkonfigurationen. Hier ein Beispiel:
C/C++
// 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);
Java
public class Result<T> {
public T getResult();
}
public class DataProcessor {
public Result<Integer> processIntegerData();
public Result<Float> processFloatData();
}
Schnittstellen implementieren
Implementieren Sie eine C-Schnittstelle, indem Sie implementInterface()
aufrufen und Folgendes angeben:
-Rückruffunktion für jede Schnittstellenmethode verwenden. Nur Schnittstellen dürfen implementiert werden.
auf diese Weise verwendet werden. Klassen und abstrakte Klassen werden nicht unterstützt. Weitere Informationen finden Sie in der
folgendes Beispiel:
C/C++
// 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);
Java
public interface Observer {
void onAction1();
void onAction2(int data);
}
public class Subject {
public void registerObserver(Observer observer);
}
Verwendungsbeispiel:
void onAction1() {
// Handle action 1
}
void onAction2(int32_t data) {
// Handle action 2
}
Observer* observer = Observer_implementInterface(onAction1, onAction2);
Subject_registerObserver(subject, observer);
Beschränkungen
Das Bibliotheks-Wrapper-Tool befindet sich in der Betaphase. Möglicherweise stoßen Sie auf Folgendes: Einschränkungen:
Nicht unterstützte Java-Konstrukte
Die folgenden Konstrukte werden in der Betaversion des Bibliothek-Wrappers nicht unterstützt:
Methodenüberlastung
In der Programmiersprache C können nicht zwei Funktionen mit demselben Namen deklariert werden. Wenn Wenn die Klasse eine Methodenüberlastung verwendet, wird der generierte C-Code nicht kompiliert. Die Sie können das Problem umgehen, indem Sie nur eine Methode mit ausreichenden Parametern verwenden. Die übrigen Funktionen lassen sich mit Filtern herausfiltern. Dieses gilt auch für Konstruktoren.
Vorlagenbasierte Methoden
Andere Felder als
static final int
undstatic final String
Arrays
Mögliche Namenskonflikte
Aufgrund der Art und Weise, wie Java-Klassen in C-Code dargestellt werden, kann es Namen geben,
zu Konflikten führen. Beispiel: Eine Klasse Foo<Bar>
und ein Inner
Die Klassen Bar
innerhalb einer Foo
-Klasse werden in C durch dasselbe Symbol dargestellt:
typedef struct Foo_Bar_ Foo_Bar;
Support
Wenn Sie ein Problem mit dem Bibliotheks-Wrapper feststellen, teilen Sie uns dies bitte mit.
In Programmfehlern suchen | Fehler melden |
---|---|
Technik | bug_report |
Dokumentation | bug_report |