Leitfaden zum Wrapper für Bibliothek

In diesem Leitfaden wird die Verwendung des Wrappers der Android API-Bibliothek beschrieben. Bibliothek Wrapper-Befehlszeilentool generiert Wrapper-Code in C-Sprache für Java Android APIs, mit denen Sie Java-Bibliotheken in native C/C++ Android-Apps integrieren können. Weitere Informationen zum Bibliotheks-Wrapper finden Sie unter Bibliotheks-Wrapper für Android APIs

In dieser detaillierten Anleitung wird gezeigt, wie Sie mit dem Wrapper-Tool ein Java-Bibliothek in eine native Android-App einbinden. In diesem Leitfaden geht es beispielsweise um Integrieren der Benachrichtigungsbibliothek des Pakets androidx.core.app. Weitere Informationen zu dieser Bibliothek findest du unter Benachrichtigung erstellen.

Voraussetzungen

In diesem Leitfaden wird davon ausgegangen, dass Sie bereits ein natives Android-Projekt haben. Außerdem verwendet das Gradle-Build-System. Wenn Sie noch kein Projekt haben, erstellen Sie ein in Android Studio mit der Vorlage Native C++ ein.

Für den Beispielcode in diesem Leitfaden wird der Verzeichnisstamm my_project/ verwendet. Nativ Code befindet sich in my_project/app/src/main/cpp/, dem Standardverzeichnis für Android Studio-Projekte

Wenn Sie das Bibliotheks-Wrapper-Tool noch nicht haben, laden Sie die Datei herunter und entpacken Sie sie. -Paket in das Verzeichnis Ihrer Wahl. Dieses CLI-Tool erfordert die Java-Laufzeit Umgebung (JRE):

Nativen Code generieren

Verwenden Sie bei der Integration einer Java-Bibliothek das Wrapper-Tool, um einen nativen Code-Wrapper zu generieren. Der erste Schritt besteht darin, den Wrapper zu konfigurieren.

Wrapper-Konfiguration erstellen

Sie erstellen Bibliotheks-Wrapper-Konfigurationsdateien, um die Ausgabe des nativen Codegenerators. Mit einer Funktion dieser Datei können Sie die Klassen und Methoden zum Generieren von Wrapper-Code.

Da es nicht viele Zusammenfassungsmethoden für die Benachrichtigungsbibliothek gibt, können Sie definieren Sie sie direkt im Abschnitt custom_classes. Erstellen Sie ein neues config.json an einer beliebigen Stelle in Ihrem Projekt, um die Methoden zu definieren. Beispiel: können Sie my_project/library_wrapper/config.json erstellen und Folgendes einfügen: Beispielkonfiguration:

{
  "custom_classes": [
    {
      "class_name": "class java.lang.CharSequence"
    },
    {
      "class_name": "class java.lang.Object",
      "methods": [
        "java.lang.String toString()"
      ]
    },
    {
      "class_name": "class java.lang.String"
    },
    {
      "class_name": "class android.content.Context",
      "methods": [
        "java.lang.Object getSystemService(java.lang.String name)"
      ]
    },
    {
      "class_name": "class android.app.Notification"
    },
    {
      "class_name": "class android.app.NotificationManager",
      "methods": [
        "void createNotificationChannel(android.app.NotificationChannel channel)"
      ]
    },
    {
      "class_name": "class android.app.NotificationChannel",
      "methods": [
        "NotificationChannel(java.lang.String id, java.lang.CharSequence name, int importance)",
        "void setDescription(java.lang.String description)"
      ]
    },
    {
      "class_name": "class androidx.core.app.NotificationCompat"
    },
    {
      "class_name": "class androidx.core.app.NotificationCompat$Builder",
      "methods": [
        "Builder(android.content.Context context, java.lang.String channelId)",
        "androidx.core.app.NotificationCompat$Builder setContentText(java.lang.CharSequence text)",
        "androidx.core.app.NotificationCompat$Builder setContentTitle(java.lang.CharSequence title)",
        "androidx.core.app.NotificationCompat$Builder setSmallIcon(int icon)",
        "androidx.core.app.NotificationCompat$Builder setPriority(int pri)",
        "android.app.Notification build()"
      ]
    },
    {
      "class_name": "class androidx.core.app.NotificationManagerCompat",
      "methods": [
        "static androidx.core.app.NotificationManagerCompat from(android.content.Context context)",
        "void notify(int id, android.app.Notification notification)"
      ]
    }
  ]
}

Im vorherigen Beispiel deklarieren Sie direkt die Java-Klassen und -Methoden, die der native Wrapper-Code erforderlich ist.

Bibliotheks-Wrapper ausführen

Nachdem Sie die Wrapper-Konfigurationsdatei definiert haben, können Sie das Tool verwenden, um des nativen Wrapper-Codes. Terminal öffnen, in das Sie den Bibliotheks-Wrapper extrahiert haben und führen Sie den folgenden Befehl aus:

java -jar lw.jar \
  -o "my_project/app/src/main/cpp/native_wrappers" \
  -c "my_project/library_wrapper/config.json"

Im vorherigen Beispiel verwenden Sie den Parameter -c, um den Wrapper anzugeben. Konfigurationsspeicherort und den Parameter -o, um das generierte Codeverzeichnis zu definieren. Nachdem Sie das Tool ausgeführt haben, sollten Sie über den generierten Code verfügen, der zum Aufrufen der Java-basierte Notifications API aus deiner nativen App.

Native Benachrichtigungen implementieren

In diesem Abschnitt binden Sie die Android-Benachrichtigungsbibliothek in Ihr nativen App mithilfe des generierten Wrapper-Codes. Der erste Schritt besteht darin, Die Ressource vom Typ gradle.build auf App-Ebene (my_project/app/gradle.build) des Projekts.

gradle.build aktualisieren

  1. GNI ist eine Supportbibliothek, die für den generierten Wrapper-Code erforderlich ist. Alle Projekte mit generierten Code sollten auf diese Bibliothek verweisen. Um auf diese Bibliothek zu verweisen, Fügen Sie die folgende Zeile in den Abschnitt dependencies von build.gradle ein:

    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    
  2. Fügen Sie dem Abschnitt android den folgenden Code hinzu, um die Unterstützung von prefab zu aktivieren:

    buildFeatures {
      prefab true
    }
    
  3. Verwenden Sie zum Konfigurieren von cmake die folgende cmake-Konfiguration im Abschnitt android/defaultConfig:

    externalNativeBuild {
      cmake {
          arguments '-DANDROID_STL=c++_shared'
      }
    }
    

Die fertige build.gradle-Konfiguration sollte in etwa so aussehen:

android {
    ...

    buildFeatures {
        prefab true
    }

    defaultConfig {
        ...

        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_shared'
            }
        }
    }
}

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    ...
}

CMakeLists ändern

  1. Fügen Sie die GNI-Bibliothek zur Datei CMakeLists.txt Ihres Projekts hinzu. (my_project/app/src/main/cpp/CMakeLists.txt) durch Hinzufügen der folgenden Zeile unter auf der obersten Ebene der Datei:

    find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
    
  2. Fügen Sie dem Abschnitt target_link_libraries die folgende Zeile hinzu:

    PUBLIC com.google.android.gms.gni.c::gni_shared
    
  3. Fügen Sie dem generierten Code einen Verweis hinzu, indem Sie die folgende Zeile am auf der obersten Ebene der Datei:

    file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
    
  4. Fügen Sie am Ende der Datei die folgenden Zeilen hinzu:

    include_directories(./native_wrappers/c)
    include_directories(./native_wrappers/cpp)
    

Die aktualisierte CMakeLists.txt-Ressource sollte in etwa so aussehen:

cmake_minimum_required(VERSION 3.18.1)

project("my_project")

file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")

add_library(
        my_project
        SHARED
        native-lib.cpp
        ${native_wrappers}
        )

find_library(
        log-lib
        log)

find_package(com.google.android.gms.gni.c REQUIRED CONFIG)

target_link_libraries(
        my_project
        PUBLIC com.google.android.gms.gni.c::gni_shared
        ${log-lib})

include_directories(./native_wrappers/c)
include_directories(./native_wrappers/cpp)

Benachrichtigungslogik implementieren

  1. Öffnen oder erstellen Sie die Quelldatei, in der Sie die Benachrichtigung implementieren möchten. Funktionen. Fügen Sie in diese Datei die Headerdatei gni.h ein und definieren Sie neue ShowNativeNotification()-Funktion:

    #include "gni/gni.h"
    
    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
      // Get the JavaVM from the JNIEnv.
      JavaVM *java_vm;
      env->GetJavaVM(&java_vm);
    
      // Initialize the GNI runtime. This function needs to be called before any
      // call to the generated code.
      GniCore_init(java_vm, main_activity);
    }
    
  2. Benachrichtigungsspezifische konstante Werte und die Benachrichtigung Handler-Funktionen CharSequenceFromCString() und CreateNotification():

    C

    const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
    const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
    const int32_t NOTIFICATION_ID = 123;  // User defined notification id.
    
    // Convert a C string into CharSequence.
    CharSequence *CharSequenceFromCString(const char *text) {
       String *string = String_fromCString(text);
       // Cast String to CharSequence. In Java, a String implements CharSequence.
       CharSequence *result = GNI_CAST(CharSequence, String, string);
       // Casting creates a new object, so it needs to be destroyed as normal.
       String_destroy(string);
       return result;
    }
    
    // Create a notification.
    Notification *
    CreateNotification(Context *context, String *channel_id,
                       const char *title, const char *content,
                       int32_t icon_id) {
       // Convert C strings to CharSequence.
       CharSequence *title_chars = CharSequenceFromCString(title);
       CharSequence *content_chars = CharSequenceFromCString(content);
    
       // Create a NotificationCompat.Builder and set all required properties.
       NotificationCompat_Builder *notification_builder =
           NotificationCompat_Builder_construct(context, channel_id);
       NotificationCompat_Builder_setContentTitle(notification_builder,
                                                  title_chars);
       NotificationCompat_Builder_setContentText(notification_builder,
                                                 content_chars);
       NotificationCompat_Builder_setSmallIcon(notification_builder, icon_id);
       NotificationCompat_Builder_setPriority(notification_builder,
                                              PRIORITY_MAX);
    
       // Build a notification.
       Notification *notification =
           NotificationCompat_Builder_build(notification_builder);
    
       // Clean up allocated objects.
       NotificationCompat_Builder_destroy(notification_builder);
       CharSequence_destroy(title_chars);
       CharSequence_destroy(content_chars);
    
       return notification;
    }
    

    C++

    const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
    const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
    const int32_t NOTIFICATION_ID = 123;  // User defined notification id.
    
    // Convert a C string into CharSequence.
    CharSequence *CharSequenceFromCString(const char *text) {
       String *string = String_fromCString(text);
       // Cast String to CharSequence. In Java, a String implements CharSequence.
       CharSequence *result = new CharSequence(string->GetImpl());
       // Casting creates a new object, so it needs to be destroyed as normal.
       String::destroy(string);
       return result;
    }
    
    // Create a notification.
    Notification&
    CreateNotification(Context *context, String *channel_id, const char *title,
                       const char *content, int32_t icon_id) {
       // Convert C strings to CharSequence.
       CharSequence *title_chars = CharSequenceFromCString(title);
       CharSequence *content_chars = CharSequenceFromCString(content);
    
       // Create a NotificationCompat.Builder and set all required properties.
    
       NotificationCompat::Builder *notification_builder = new NotificationCompat::Builder(*context, *channel_id);
       notification_builder->setContentTitle(*title_chars);
       notification_builder->setContentText(*content_chars);
       notification_builder->setSmallIcon(icon_id);
       notification_builder->setPriority(PRIORITY_MAX);
    
       // Build a notification.
       Notification& notification = notification_builder->build();
    
       // Clean up allocated objects.
       NotificationCompat::Builder::destroy(notification_builder);
       CharSequence::destroy(title_chars);
       CharSequence::destroy(content_chars);
    
       return notification;
    }
    

    Einige Funktionen der Benachrichtigungsbibliothek verwenden CharSequence anstelle von String. Die Funktion CharSequenceFromCString() ermöglicht die Konvertierung zwischen für diese Objekte. Die Funktion CreateNotification() verwendet die umschlossene Version von Java NotificationCompat.Builder zum Erstellen einer Benachrichtigung.

  3. Fügen Sie Logik zum Erstellen eines Benachrichtigungskanals hinzu, indem Sie Folgendes einfügen: CreateNotificationChannel()-Funktion:

    C

    void CreateNotificationChannel(Context *context, String *channel_id) {
       CharSequence *channel_name = CharSequenceFromCString("channel name");
       String *channel_description = String_fromCString("channel description");
       String *system_service_name = String_fromCString("notification");
       NotificationChannel *channel =
           NotificationChannel_construct(channel_id, channel_name,
                                         IMPORTANCE_HIGH);
       NotificationChannel_setDescription(channel, channel_description);
    
       Object *notification_manager_as_object =
           Context_getSystemService(context, system_service_name);
       NotificationManager *notification_manager =
           GNI_CAST(NotificationManager, Object,
                    notification_manager_as_object);
    
       NotificationManager_createNotificationChannel(notification_manager,
                                                     channel);
    
       CharSequence_destroy(channel_name);
       String_destroy(channel_description);
       String_destroy(system_service_name);
       NotificationChannel_destroy(channel);
       Object_destroy(notification_manager_as_object);
       NotificationManager_destroy(notification_manager);
    }
    

    C++

    void CreateNotificationChannel(Context *context, String *channel_id) {
       CharSequence *channel_name = CharSequenceFromCString("channel name");
       String *channel_description = String_fromCString("channel description");
       String *system_service_name = String_fromCString("notification");
       NotificationChannel *channel =
           new NotificationChannel(*channel_id, *channel_name, IMPORTANCE_HIGH);
       channel->setDescription(*channel_description);
    
       Object& notification_manager_as_object =
           context->getSystemService(*system_service_name);
       NotificationManager *notification_manager =
           new NotificationManager(notification_manager_as_object.GetImpl());
    
       notification_manager->createNotificationChannel(*channel);
    
       CharSequence::destroy(channel_name);
       String::destroy(channel_description);
       String::destroy(system_service_name);
       NotificationChannel::destroy(channel);
       Object::destroy(&notification_manager_as_object);
       NotificationManager::destroy(notification_manager);
    }
    
  4. Aktualisieren Sie die zuvor erstellte Funktion ShowNativeNotification() auf Rufen Sie CreateNotificationChannel() an. Fügen Sie den folgenden Code am Ende der ShowNativeNotification():

    C

    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
     // ...
    
     // Create a Context object by wrapping an existing JNI reference.
     Context *context = Context_wrapJniReference(main_activity);
    
     // Create a String object.
     String *channel_id = String_fromCString("new_messages");
    
     // Create a notification channel.
     CreateNotificationChannel(context, channel_id);
    
     // Create a notification with a given title, content, and icon.
     Notification *notification =
         CreateNotification(context, channel_id, "My Native Notification",
                            "Hello!", icon_id);
    
     // Create a notification manager and use it to show the notification.
     NotificationManagerCompat *notification_manager =
         NotificationManagerCompat_from(context);
     NotificationManagerCompat_notify(notification_manager, NOTIFICATION_ID,
                                      notification);
    
     // Destroy all objects.
     Context_destroy(context);
     String_destroy(channel_id);
     Notification_destroy(notification);
     NotificationManagerCompat_destroy(notification_manager);
    }
    

    C++

    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
       // Get the JavaVM from the JNIEnv.
       JavaVM *java_vm;
       env->GetJavaVM(&java_vm);
    
       // Initialize the GNI runtime. This function needs to be called before any
       // call to the generated code.
       GniCore::Init(java_vm, main_activity);
    
       // Create a Context object by wrapping an existing JNI reference.
       Context *context = new Context(main_activity);
    
       // Create a String object.
       String *channel_id = String_fromCString("new_messages");
    
       // Create a notification channel.
       CreateNotificationChannel(context, channel_id);
    
       // Create a notification with a given title, content, and icon.
       Notification& notification =
           CreateNotification(context, channel_id, "My Native Notification",
                              "Hello!", icon_id);
    
       // Create a notification manager and use it to show the notification.
       NotificationManagerCompat& notification_manager =
           NotificationManagerCompat::from(*context);
       notification_manager.notify(NOTIFICATION_ID, notification);
    
       // Destroy all objects.
       Context::destroy(context);
       String::destroy(channel_id);
       Notification::destroy(&notification);
       NotificationManagerCompat::destroy(&notification_manager);
    }   
  5. Wenn Ihre Logik definiert ist, lösen Sie eine Benachrichtigung aus, indem Sie folgenden Befehl aufrufen: ShowNativeNotification() an einem geeigneten Speicherort in Ihrem Projekt.

App ausführen

Kompilieren Sie den Code und führen Sie ihn aus, der ShowNativeNotification() aufruft. Eine einfache sollte oben auf dem Bildschirm des Testgeräts eine entsprechende Benachrichtigung angezeigt werden.

Wrapper aus JARs generieren

Im vorherigen Beispiel haben Sie manuell Java-Klassen und Methoden, die nativen Code in einer Wrapper-Konfigurationsdatei erfordern. Für Szenarien, in denen auf große Abschnitte einer API zugreifen müssen, ist es effizienter, weitere Bibliotheks-JARs zum Wrapper-Tool hinzu. Der Wrapper generiert dann Wrapper für alle öffentlichen Symbole, die sie in der JAR-Datei finden.

Im folgenden Beispiel wird die gesamte Notifications API zusammengefasst, indem eine Bibliothek bereitgestellt wird. JAR-Datei.

Erforderliche JARs abrufen

Die Notification API ist Teil des androidx.core-Pakets, das über Das Google Maven-Repository. Laden Sie die aar-Datei der Bibliothek herunter und entpacken Sie sie in ein Verzeichnis Ihrer Wahl. Suchen Sie die Datei classes.jar.

Die Datei classes.jar enthält neben den erforderlichen Benachrichtigungen noch viele weitere Klassen. Bibliothek. Wenn Sie den Bibliotheks-Wrapper nur mit classes.jar bereitstellen, wird das Tool generiert nativen Code für jede Klasse in der JAR-Datei, was ineffizient ist und für unser Projekt unnötig sind. Um dieses Problem zu lösen, stellen Sie dem Wrapper-Konfiguration zur Beschränkung der Codegenerierung auf die JAR-Benachrichtigung Klassen.

Zulassungsfilter definieren

Filterdateien sind Nur-Text-Dateien, die Sie im Bibliotheks-Wrapper bereitstellen. Konfiguration. Sie ermöglichen es, zu definieren, welche Klassen ein- oder ausgeschlossen werden sollen. aus JAR-Dateien, die an den Bibliotheks-Wrapper bereitgestellt werden.

Erstellen Sie in Ihrem Projekt eine Datei mit dem Namen allowed-symbols.txt und fügen Sie den folgende Zeile:

androidx.core.app.NotificationCompat*

Bei Verwendung als Zulassungsfilter gibt der vorherige Code an, dass nur Symbole deren Namen mit androidx.core.app.NotificationCompat beginnen, sind umschlossen.

Bibliotheks-Wrapper ausführen

Öffnen Sie ein Terminal für das JAR-Verzeichnis und führen Sie den folgenden Befehl aus:

java -jar lw.jar \
 -i classes.jar \
 -o "./generated-jar" \
 -c "./config.json" \
 -fa allowed-symbols.txt \
 --skip_deprecated_symbols

Mit dem vorherigen Beispielbefehl wird Wrapper-Code für Ihre gefilterten Klassen generiert in das Verzeichnis generated-jar/.

Support

Wenn Sie ein Problem mit dem Bibliotheks-Wrapper feststellen, teilen Sie uns dies bitte mit.

In Programmfehlern suchen Fehler melden
Technik
Dokumentation