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 und static 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
Dokumentation