In diesem Leitfaden wird die Verwendung des Wrappers der Android API-Bibliothek beschrieben. Das Befehlszeilentool „Library Wrapper“ generiert Wrapper-Code in C-Sprache für Java Android APIs, sodass Sie Java-Bibliotheken in native C/C++ Android-Apps einbinden können. Weitere Informationen zum Bibliotheks-Wrapper findest du unter Bibliothek-Wrapper für Android APIs.
In dieser detaillierten Anleitung wird beschrieben, wie Sie mit dem Wrapper-Tool eine Java-Bibliothek in eine native Android-App einbinden. In dieser Anleitung wird beispielsweise die Integration der Benachrichtigungsbibliothek im androidx.core.app
-Paket beschrieben.
Weitere Informationen zu dieser Bibliothek findest du unter Benachrichtigung erstellen.
Voraussetzungen
In dieser Anleitung wird davon ausgegangen, dass Sie bereits ein natives Android-Projekt haben. Außerdem wird dafür das Build-System Gradle verwendet. Wenn du noch kein Projekt hast, erstelle ein neues in Android Studio mit der Vorlage Native C++.
Im Beispielcode in diesem Leitfaden wird der Verzeichnisstamm my_project/
verwendet. Der native 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 das Paket herunter und entpacken Sie es im Verzeichnis Ihrer Wahl. Für dieses Befehlszeilen-Tool ist die Java-Laufzeitumgebung (JRE) erforderlich.
Nativen Code generieren
Verwenden Sie beim Einbinden 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 Konfigurationsdateien für Bibliotheks-Wrapper, um die Ausgabe des nativen Codegenerators zu steuern. Mit einer Funktion dieser Datei können Sie die Klassen und Methoden zum Generieren von Wrapper-Code angeben.
Da es nicht viele Methoden zum Zusammenfassen für die Benachrichtigungsbibliothek gibt, können Sie diese direkt im Abschnitt custom_classes
definieren. Erstellen Sie an einer beliebigen Stelle in Ihrem Projekt eine neue config.json
-Ressource, um die Methoden zu definieren. Sie können beispielsweise my_project/library_wrapper/config.json
erstellen und die folgende Beispielkonfiguration einfügen:
{
"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 nativen Wrapper-Code erfordern.
Bibliothek-Wrapper ausführen
Nachdem Sie die Wrapper-Konfigurationsdatei definiert haben, können Sie mit dem Tool nativen Wrapper-Code generieren. Öffnen Sie ein Terminal, in das Sie den Bibliothek-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 Speicherort der Wrapper-Konfiguration anzugeben, und den Parameter -o
, um das Verzeichnis mit dem generierten Code zu definieren.
Nachdem Sie das Tool ausgeführt haben, sollten Sie den generierten Code haben, der zum Aufrufen der Java-basierten Notifications API aus Ihrer nativen App erforderlich ist.
Native Benachrichtigungen implementieren
In diesem Abschnitt integrieren Sie die Android-Benachrichtigungsbibliothek mithilfe des generierten Wrapper-Codes in Ihre native App. Der erste Schritt besteht darin, die gradle.build
-Ressource Ihres Projekts (my_project/app/gradle.build
) auf App-Ebene zu aktualisieren.
gradle.build
aktualisieren
GNI ist eine Supportbibliothek, die für den generierten Wrapper-Code benötigt wird. Alle Projekte, die generierten Code verwenden, sollten auf diese Bibliothek verweisen. Fügen Sie dem Abschnitt
dependencies
vonbuild.gradle
die folgende Zeile hinzu, um auf diese Bibliothek zu verweisen:implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
Um die prefab-Unterstützung zu aktivieren, fügen Sie den folgenden Code in den Abschnitt
android
ein:buildFeatures { prefab true }
Verwenden Sie die folgende
cmake
-Konfiguration im Abschnittandroid/defaultConfig
, umcmake
zu konfigurieren:externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_shared' } }
Ihre abgeschlossene 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
Fügen Sie die GNI-Bibliothek zum
CMakeLists.txt
Ihres Projekts (my_project/app/src/main/cpp/CMakeLists.txt
) hinzu, indem Sie die folgende Zeile auf oberster Ebene der Datei einfügen:find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
Fügen Sie dem Abschnitt
target_link_libraries
die folgende Zeile hinzu:PUBLIC com.google.android.gms.gni.c::gni_shared
Fügen Sie dem generierten Code einen Verweis hinzu, indem Sie die folgende Zeile auf der obersten Ebene der Datei hinzufügen:
file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
Fügen Sie diese Zeilen am Ende der Datei ein:
include_directories(./native_wrappers/c) include_directories(./native_wrappers/cpp)
Die aktualisierte Ressource CMakeLists.txt
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
Öffnen oder erstellen Sie die Quelldatei, in der Sie Benachrichtigungsfunktionen implementieren möchten. Fügen Sie in dieser Datei die Headerdatei
gni.h
ein und definieren Sie eine neueShowNativeNotification()
-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); }
Definieren Sie benachrichtigungsspezifische konstante Werte und der Benachrichtigungs-Handler funktioniert
CharSequenceFromCString()
undCreateNotification()
: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 vonString
. Die FunktionCharSequenceFromCString()
ermöglicht die Konvertierung zwischen diesen Objekten. Die FunktionCreateNotification()
verwendet die umschlossene Version von JavaNotificationCompat.Builder
, um eine Benachrichtigung zu erstellen.Fügen Sie die Logik zum Erstellen eines Benachrichtigungskanals hinzu, indem Sie die folgende Funktion
CreateNotificationChannel()
einfügen: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(¬ification_manager_as_object); NotificationManager::destroy(notification_manager); }
Aktualisieren Sie die zuvor erstellte Funktion
ShowNativeNotification()
, umCreateNotificationChannel()
aufzurufen. Fügen Sie am Ende vonShowNativeNotification()
den folgenden Code ein: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(¬ification); NotificationManagerCompat::destroy(¬ification_manager); }
Wenn Ihre Logik definiert ist, können Sie eine Benachrichtigung auslösen, indem Sie
ShowNativeNotification()
an einer entsprechenden Stelle im Projekt aufrufen.
App ausführen
Kompilieren Sie den Code, der ShowNativeNotification()
aufruft, und führen Sie ihn aus. Oben auf dem Bildschirm des Testgeräts sollte eine einfache Benachrichtigung angezeigt werden.
Wrapper aus JARs generieren
Im vorherigen Beispiel haben Sie Java-Klassen und -Methoden, die nativen Code in einer Wrapper-Konfigurationsdatei erfordern, manuell definiert. Wenn Sie auf große Bereiche einer API zugreifen müssen, ist es effizienter, dem Wrapper-Tool eine oder mehrere Bibliotheks-JARs bereitzustellen. Der Wrapper generiert dann Wrapper für alle in der JAR-Datei gefundenen öffentlichen Symbole.
Im folgenden Beispiel wird die gesamte Notifications API umschlossen, indem eine Bibliotheks-JAR-Datei bereitgestellt wird.
Besorgen Sie sich die erforderlichen JARs
Die Notification API ist Teil des Pakets androidx.core
, das im Google Maven-Repository verfügbar ist. Laden Sie die aar-Bibliotheksdatei herunter und entpacken Sie sie in ein Verzeichnis Ihrer Wahl. Suchen Sie die Datei classes.jar
.
Die Datei classes.jar
enthält viele Klassen außerhalb der erforderlichen Benachrichtigungsbibliothek. Wenn Sie den Bibliothek-Wrapper nur mit classes.jar
bereitstellen, generiert das Tool nativen Code für jede Klasse in der JAR-Datei. Dies ist für unser Projekt ineffizient und unnötig. Stellen Sie zur Behebung dieses Problems eine Filterdatei für die Wrapper-Konfiguration bereit, um die Codegenerierung auf die Benachrichtigungsklassen der JAR-Datei zu beschränken.
Zulassungsfilter definieren
Filterdateien sind Nur-Text-Dateien, die Sie in Ihrer Bibliotheks-Wrapper-Konfiguration angeben. Damit können Sie definieren, welche Klassen in die JAR-Dateien, die für den Bibliotheks-Wrapper bereitgestellt werden, einbezogen oder davon ausgeschlossen werden sollen.
Erstellen Sie in Ihrem Projekt eine Datei mit dem Namen allowed-symbols.txt
und fügen Sie sie in die folgende Zeile ein:
androidx.core.app.NotificationCompat*
Bei Verwendung als „allow“-Filter gibt der vorherige Code an, dass nur Symbole, deren Name mit androidx.core.app.NotificationCompat
beginnt, umschlossen werden.
Bibliothek-Wrapper ausführen
Öffnen Sie ein Terminal zum 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 im Verzeichnis generated-jar/
generiert.
Support
Wenn Sie ein Problem mit dem Bibliotheks-Wrapper feststellen, teilen Sie uns dies bitte mit.
Programmfehler durchsuchen | Programmfehler melden |
---|---|
Technik | bug_report |
Dokumentation | bug_report |