Questa guida descrive come utilizzare il wrapper della libreria API Android. La biblioteca lo strumento a riga di comando wrapper genera un codice wrapper C-lingua per Java Android che ti consentono di integrare librerie Java nelle app per Android C/C++ native. Per maggiori dettagli sul wrapper libreria, consulta Wrapper libreria per le API Android.
Questa guida passo passo illustra come utilizzare lo strumento wrapper per integrare
una libreria Java in un'app Android nativa. Ad esempio, questa guida tratta
integrando la libreria di notifica del pacchetto androidx.core.app
.
Per scoprire di più su questa raccolta, consulta l'articolo Create a Notification (Crea una notifica).
Prerequisiti
Questa guida presuppone che tu abbia un progetto Android nativo esistente. Inoltre, utilizza il sistema di compilazione Gradle. Se non hai già un progetto, crea un una nuova in Android Studio utilizzando il modello C++ nativo.
Il codice di esempio in questa guida utilizza la directory radice my_project/
. Nativi
si trova in my_project/app/src/main/cpp/
, la directory predefinita
progetti Android Studio.
Se non hai ancora lo strumento per il wrapper libreria, scarica e decomprimi nella directory che preferisci. Questo strumento di interfaccia a riga di comando richiede il runtime Java Ambiente (JRE).
Genera codice nativo
Durante l'integrazione di una libreria Java, utilizza lo strumento wrapper generare un wrapper di codice nativo. Il primo passaggio prevede la configurazione del wrapper.
Crea la configurazione del wrapper
Devi creare file di configurazione del wrapper libreria per controllare dell'output del generatore di codice nativo. Una funzionalità di questo file ti consente di specificare le classi e i metodi per generare il codice wrapper.
Poiché non sono disponibili molti metodi per il wrapping della raccolta delle notifiche, puoi
puoi definirli direttamente nella sezione custom_classes
. Crea un nuovo elemento
config.json
risorsa in qualsiasi punto del progetto per definire i metodi. Ad esempio:
puoi creare my_project/library_wrapper/config.json
e incollare quanto segue
configurazione di esempio:
{
"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)"
]
}
]
}
Nell'esempio precedente, dichiari direttamente le classi e i metodi Java che richiedono un codice wrapper nativo.
Esegui il wrapper libreria
Una volta definito il file di configurazione del wrapper puoi utilizzare lo strumento per generare il codice wrapper nativo. Apri un terminale da cui hai estratto il wrapper della libreria ed esegui questo comando:
java -jar lw.jar \
-o "my_project/app/src/main/cpp/native_wrappers" \
-c "my_project/library_wrapper/config.json"
Nell'esempio precedente, utilizzi il parametro -c
per specificare il wrapper
della configurazione e il parametro -o
per definire la directory del codice generata.
Dopo aver eseguito lo strumento, dovresti avere il codice generato necessario per chiamare la funzione
L'API di notifica basata su Java dalla tua app nativa.
Implementare le notifiche native
In questa sezione, integri la libreria delle notifiche di Android nel tuo
utilizzando il codice wrapper generato. Il primo passaggio consiste nell'aggiornare
risorsa gradle.build
a livello di app del progetto (my_project/app/gradle.build
).
Aggiorna gradle.build
GNI è una libreria di supporto richiesta dal codice wrapper generato. Tutti i progetti che utilizza il codice generato deve fare riferimento a questa libreria. Per fare riferimento a questa libreria, aggiungi la seguente riga alla sezione
dependencies
dibuild.gradle
:implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
Per attivare il supporto prefab, aggiungi il seguente codice alla sezione
android
:buildFeatures { prefab true }
Per configurare
cmake
, utilizza la seguente configurazione dicmake
nel Sezioneandroid/defaultConfig
:externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_shared' } }
La configurazione di build.gradle
completata dovrebbe essere simile alla seguente:
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'
...
}
Modifica CMakeLists
Aggiungi la libreria GNI a
CMakeLists.txt
del tuo progetto (my_project/app/src/main/cpp/CMakeLists.txt
) aggiungendo la seguente riga alla al livello superiore del file:find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
Aggiungi la seguente riga alla sezione
target_link_libraries
:PUBLIC com.google.android.gms.gni.c::gni_shared
Aggiungi un riferimento al codice generato aggiungendo la riga seguente alla livello superiore del file:
file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
Aggiungi queste righe verso la fine del file:
include_directories(./native_wrappers/c) include_directories(./native_wrappers/cpp)
La risorsa CMakeLists.txt
aggiornata dovrebbe essere simile al seguente esempio:
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)
Implementa la logica di notifica
Apri o crea il file di origine in cui implementare la notifica le funzionalità di machine learning. In questo file, includi il file di intestazione
gni.h
e definisci un'istanza nuova funzioneShowNativeNotification()
:#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); }
Definisci i valori costanti specifici per la notifica e la notifica le funzioni di gestore
CharSequenceFromCString()
eCreateNotification()
: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; }
Alcune funzioni della raccolta delle notifiche utilizzano
CharSequence
anzichéString
. La funzioneCharSequenceFromCString()
consente la conversione tra di questi oggetti. La funzioneCreateNotification()
utilizza la versione con wrapping di JavaNotificationCompat.Builder
per creare una notifica.Aggiungi una logica per creare un canale di notifica incollando quanto segue funzione,
CreateNotificationChannel()
: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); }
Aggiorna la funzione
ShowNativeNotification()
creata in precedenza in chiamaCreateNotificationChannel()
. Aggiungi il seguente codice alla fine diShowNativeNotification()
: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); }
Con la logica definita, attiva una notifica chiamando
ShowNativeNotification()
in una posizione appropriata nel tuo progetto.
Esegui l'app
Compila ed esegui il codice che chiama ShowNativeNotification()
. Un semplice
dovrebbe essere visualizzata nella parte superiore dello schermo del dispositivo di test.
Genera wrapper dai JAR
Nell'esempio precedente, hai definito manualmente le classi Java e che richiedono codice nativo in un file di configurazione wrapper. Per scenari in cui accedere a grandi sezioni di un'API, è più efficiente fornire uno più JAR di libreria allo strumento wrapper. Il wrapper genera quindi wrapper per tutti i simboli pubblici che trova nel JAR.
L'esempio seguente aggrega l'intera API Notifications fornendo una libreria JAR.
Recupera i JAR richiesti
L'API Notification fa parte del pacchetto androidx.core
, disponibile da
nel Repository Google Maven. Scarica il file aar della libreria e aprilo in
una directory di tua scelta. Individua il file classes.jar
.
Il file classes.jar
contiene molti corsi oltre alle notifiche obbligatorie
libreria. Se fornisci il wrapper libreria solo con classes.jar
, lo strumento
genera codice nativo per ogni classe nel file JAR, che è inefficiente
inutili per il nostro progetto. Per risolvere il problema, fornisci un file di filtro alla
configurazione del wrapper per limitare la generazione del codice alla notifica del JAR
.
Definisci un filtro di autorizzazione
I file filtro sono file di testo normale che fornisci al wrapper della raccolta configurazione. Consentono di definire quali classi includere (o escludere) dai file JAR forniti al wrapper della libreria.
Nel tuo progetto, crea un file denominato allowed-symbols.txt
e incollalo nel
riga seguente:
androidx.core.app.NotificationCompat*
Quando utilizzato come filtro di autorizzazione, il codice precedente specifica che solo i simboli
il cui nome inizia con androidx.core.app.NotificationCompat
è aggregato.
Esegui il wrapper libreria
Apri un terminale nella directory JAR ed esegui questo comando:
java -jar lw.jar \
-i classes.jar \
-o "./generated-jar" \
-c "./config.json" \
-fa allowed-symbols.txt \
--skip_deprecated_symbols
Il comando di esempio precedente genera il codice wrapper per le classi filtrate
alla directory generated-jar/
.
Assistenza
Se riscontri un problema con il wrapper della biblioteca, faccelo sapere.
Sfoglia i bug | Segnala un bug |
---|---|
Ingegneria | bug_report |
Documentazione | bug_report |