Przewodnik po kodzie biblioteki

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

  1. 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 w build.gradle:

    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    
  2. Aby włączyć obsługę prefab, dodaj ten kod do sekcji android:

    buildFeatures {
      prefab true
    }
    
  3. Aby skonfigurować cmake, użyj następującej konfiguracji cmake w Sekcja android/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

  1. 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)
    
  2. Dodaj ten wiersz do sekcji target_link_libraries:

    PUBLIC com.google.android.gms.gni.c::gni_shared
    
  3. 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")
    
  4. 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ń

  1. 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 funkcja ShowNativeNotification():

    #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. Zdefiniuj stałe wartości dla powiadomień i określ powiadomienie funkcje obsługi CharSequenceFromCString() i 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;
    }
    

    Niektóre funkcje biblioteki powiadomień przyjmują CharSequence zamiast String Funkcja CharSequenceFromCString() umożliwia konwersję między tych obiektów. Funkcja CreateNotification() używa opakowanej wersji Java NotificationCompat.Builder, aby utworzyć powiadomienie.

  3. 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(&notification_manager_as_object);
       NotificationManager::destroy(notification_manager);
    }
    
  4. Zaktualizuj utworzoną wcześniej funkcję ShowNativeNotification() do Zadzwoń pod numer CreateNotificationChannel(). Dodaj poniższy kod na końcu argumentu 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. 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
Dokumentacja