Z tego przewodnika dowiesz się, jak korzystać z kodu biblioteki interfejsu Android API. Biblioteka narzędzie wiersza poleceń otoki generuje kod otoki w języku C na potrzeby systemu Java interfejsów API, które umożliwiają integrację bibliotek Java z natywnymi aplikacjami na Androida w języku C/C++. Więcej informacji o kodzie biblioteki znajdziesz tutaj Kod biblioteki dla interfejsów API Androida.
Ten szczegółowy przewodnik pokazuje, jak za pomocą narzędzia do kodowania zintegrować kod
z biblioteki Java do natywnej aplikacji na Androida. W tym przewodniku omawiamy m.in. te kwestie:
przez integrację biblioteki powiadomień pakietu androidx.core.app
.
Więcej informacji o tej bibliotece znajdziesz w artykule Tworzenie powiadomienia.
Wymagania wstępne
W tym przewodniku przyjęto założenie, że masz już natywny projekt na Androida. Dodatkowo korzysta z systemu kompilacji Gradle. Jeśli nie masz jeszcze projektu, utwórz w Android Studio za pomocą szablonu Natywnego języka C++.
Przykładowy kod w tym przewodniku używa katalogu głównego my_project/
. Reklama natywna
kod znajduje się w my_project/app/src/main/cpp/
, domyślnym katalogu dla
Projekty w Android Studio.
Jeśli nie masz jeszcze narzędzia do obsługi opakowań biblioteki, pobierz i rozpakuj plik do wybranego katalogu. To narzędzie interfejsu wiersza poleceń wymaga środowiska wykonawczego Java Środowisko (JRE).
Wygeneruj kod natywny
Podczas integracji biblioteki Java użyj narzędzia do obsługi opakowań, aby do wygenerowania kodu natywnego. Pierwszym krokiem jest skonfigurowanie opakowania.
Tworzenie konfiguracji opakowania
Tworzysz pliki konfiguracji otoki biblioteki, aby kontrolować z generatora kodu natywnego. Jedna z funkcji tego pliku pozwala określić klasy i metod generowania kodu.
Ponieważ w bibliotece powiadomień nie ma wielu metod dodawania tagów, możesz
zdefiniować je bezpośrednio w sekcji custom_classes
. Utwórz nowy element
config.json
zasób w dowolnym miejscu w projekcie, aby zdefiniować metody. Przykład:
możesz utworzyć my_project/library_wrapper/config.json
i wkleić to
przykładowa konfiguracja:
{
"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)"
]
}
]
}
W poprzednim przykładzie bezpośrednio zadeklarujesz klasy i metody Java, które wymagają natywnego kodu towarzyszącego.
Uruchamianie kodu biblioteki
Po zdefiniowaniu pliku konfiguracji opakowania możesz użyć narzędzia do wygenerowania natywny kod towarzyszący. Otwórz terminal, w którym został wyodrębniony kod biblioteki i uruchom to polecenie:
java -jar lw.jar \
-o "my_project/app/src/main/cpp/native_wrappers" \
-c "my_project/library_wrapper/config.json"
W poprzednim przykładzie do określenia kodu używany jest parametr -c
.
oraz parametr -o
definiujący wygenerowany katalog kodu.
Po uruchomieniu narzędzia powinien już być wygenerowany kod niezbędny do wywołania metody
Oparty na Javie interfejs API powiadomień z aplikacji natywnej.
Wdrażanie powiadomień natywnych
W tej sekcji zintegrujesz bibliotekę powiadomień Androida ze swoim
natywną przy użyciu wygenerowanego kodu opakowującego. Pierwszym krokiem jest zaktualizowanie
zasób gradle.build
(my_project/app/gradle.build
) na poziomie aplikacji na poziomie projektu.
Aktualizuj usługę gradle.build
GNI to biblioteka pomocy wymagana przez wygenerowany kod otoki. Wszystkie projekty używając wygenerowanego kodu, powinien odwoływać się do tej biblioteki. Aby odwołać się do tej biblioteki, dodaj ten wiersz do sekcji
dependencies
wbuild.gradle
:implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
Aby włączyć obsługę prefab, dodaj ten kod do sekcji
android
:buildFeatures { prefab true }
Aby skonfigurować
cmake
, użyj następującej konfiguracjicmake
w Sekcjaandroid/defaultConfig
:externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_shared' } }
Ukończona konfiguracja build.gradle
powinna wyglądać mniej więcej tak:
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'
...
}
Zmień: CMakeLists
Dodaj bibliotekę GNI do folderu
CMakeLists.txt
projektu (my_project/app/src/main/cpp/CMakeLists.txt
), dodając ten wiersz na stronie na najwyższym poziomie pliku:find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
Dodaj ten wiersz do sekcji
target_link_libraries
:PUBLIC com.google.android.gms.gni.c::gni_shared
Dodaj odwołanie do wygenerowanego kodu, dodając ten wiersz w na najwyższym poziomie pliku:
file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
Dodaj te wiersze na końcu pliku:
include_directories(./native_wrappers/c) include_directories(./native_wrappers/cpp)
Zaktualizowany zasób CMakeLists.txt
powinien przypominać ten przykład:
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)
Wdróż logikę powiadomień
Otwórz lub utwórz plik źródłowy, w którym chcesz zaimplementować powiadomienie funkcje zabezpieczeń. Dołącz do tego pliku plik nagłówka
gni.h
i zdefiniuj atrybut nowa funkcjaShowNativeNotification()
:#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); }
Zdefiniuj stałe wartości dla powiadomień i określ powiadomienie funkcje obsługi
CharSequenceFromCString()
iCreateNotification()
: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; }
Niektóre funkcje biblioteki powiadomień przyjmują
CharSequence
zamiastString
FunkcjaCharSequenceFromCString()
umożliwia konwersję między tych obiektów. FunkcjaCreateNotification()
używa opakowanej wersji JavaNotificationCompat.Builder
, aby utworzyć powiadomienie.Dodaj funkcje logiczne, aby utworzyć kanał powiadomień, wklejając ten kod funkcja,
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); }
Zaktualizuj utworzoną wcześniej funkcję
ShowNativeNotification()
do Zadzwoń pod numerCreateNotificationChannel()
. Dodaj poniższy kod na końcu argumentuShowNativeNotification()
: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); }
Po zdefiniowaniu określonych zasad aktywuj powiadomienie, wywołując
ShowNativeNotification()
w odpowiednim miejscu w projekcie.
Uruchom aplikację
Skompiluj i uruchom kod wywołujący funkcję ShowNativeNotification()
. Prosta
u góry ekranu urządzenia testowego powinno pojawić się powiadomienie.
Generuj kody z pakietów JAR
W poprzednim przykładzie zdefiniowano ręcznie klasy Java, które wymagają kodu natywnego w pliku konfiguracji opakowania. W przypadku sytuacji, gdy dostępu do dużych sekcji interfejsu API. Skuteczniejszym rozwiązaniem jest udostępnienie do narzędzia do tworzenia opakowań plików JAR z biblioteki. Następnie kod generuje kod dla: wszystkich symboli publicznych, które znajduje się w pliku JAR.
Poniższy przykład opakowuje cały interfejs Notification API, dodając bibliotekę JAR.
Pobieranie wymaganych plików JAR
Interfejs Notification API jest częścią pakietu androidx.core
, który jest dostępny z
w repozytorium Google Maven. Pobierz plik aar biblioteki i rozpakuj go
w wybranym przez Ciebie katalogu. Znajdź plik classes.jar
.
Plik classes.jar
zawiera wiele klas poza wymaganymi powiadomieniami
bibliotece. Jeśli podasz kod biblioteki tylko z tagiem classes.jar
, narzędzie
generuje niewydajny kod natywny dla każdej klasy w pliku JAR.
które są niepotrzebne w naszym projekcie. Aby rozwiązać ten problem, prześlij plik filtra do
konfiguracja opakowań w celu ograniczenia generowania kodu do powiadomienia JAR
zajęcia.
Zdefiniuj filtr zezwalający
Pliki filtrów to zwykłe pliki tekstowe, które przesyłasz do kodu biblioteki. konfiguracji. Umożliwiają one określenie, które klasy mają być uwzględniane (lub wykluczane). z plików JAR przekazanych do kodu biblioteki.
Utwórz w projekcie plik o nazwie allowed-symbols.txt
i wklej
ten wiersz:
androidx.core.app.NotificationCompat*
Jeśli jest używany jako filtr zezwalający, poprzedni kod określa, że tylko symbole
których nazwa zaczyna się od androidx.core.app.NotificationCompat
, są zapakowane.
Uruchamianie kodu biblioteki
Otwórz terminal w katalogu JAR i uruchom to polecenie:
java -jar lw.jar \
-i classes.jar \
-o "./generated-jar" \
-c "./config.json" \
-fa allowed-symbols.txt \
--skip_deprecated_symbols
Poprzednie przykładowe polecenie generuje kod otoki dla odfiltrowanych klas
do katalogu generated-jar/
.
Pomoc
Jeśli zauważysz problem z kodem biblioteki, daj nam znać.
Przeglądaj błędy | Zgłoś błąd |
---|---|
Inżynieria | bug_report |
Dokumentacja | bug_report |