Panduan wrapper library

Panduan ini menjelaskan cara menggunakan wrapper library API Android. Alat command line wrapper library menghasilkan kode wrapper bahasa C untuk API Android Java, memungkinkan Anda mengintegrasikan library Java ke aplikasi Android C/C++ native. Untuk detail selengkapnya tentang wrapper library, lihat Wrapper library untuk API Android.

Panduan langkah demi langkah ini menunjukkan cara menggunakan alat wrapper untuk mengintegrasikan library Java ke dalam aplikasi Android native. Misalnya, panduan ini mencakup integrasi library notifikasi paket androidx.core.app. Lihat Membuat Notifikasi untuk mempelajari library ini lebih lanjut.

Prasyarat

Panduan ini mengasumsikan bahwa Anda sudah memiliki project Android native. Panduan ini juga menggunakan sistem build Gradle. Jika Anda belum memiliki project, buat project baru di Android Studio menggunakan template Native C++.

Kode contoh dalam panduan ini menggunakan root direktori my_project/. Kode native terletak di my_project/app/src/main/cpp/, direktori default untuk project Android Studio.

Jika belum memiliki alat wrapper library, download dan ekstrak paket ke direktori pilihan Anda. Alat CLI ini memerlukan Java Runtime Environment (JRE).

Membuat kode native

Saat mengintegrasikan library Java, gunakan alat wrapper untuk membuat wrapper kode native. Langkah pertama adalah mengonfigurasi wrapper.

Membuat konfigurasi wrapper

Anda membuat file konfigurasi wrapper library untuk mengontrol output generator kode native. Salah satu fitur file ini memungkinkan Anda menentukan class dan metode untuk menghasilkan kode wrapper.

Karena tidak ada banyak metode yang dapat digabungkan untuk library notifikasi, Anda dapat menentukannya secara langsung di bagian custom_classes. Buat resource config.json baru di mana saja dalam project Anda untuk menentukan metodenya. Misalnya, Anda dapat membuat my_project/library_wrapper/config.json dan menempelkan konfigurasi contoh berikut:

{
  "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)"
      ]
    }
  ]
}

Pada contoh sebelumnya, Anda langsung mendeklarasikan class dan metode Java yang memerlukan kode wrapper native.

Menjalankan wrapper library

Setelah file konfigurasi wrapper Anda ditentukan, Anda siap menggunakan alat ini untuk menghasilkan kode wrapper native. Buka terminal tempat Anda mengekstrak wrapper library dan jalankan perintah berikut:

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

Dalam contoh sebelumnya, Anda menggunakan parameter -c untuk menentukan lokasi konfigurasi wrapper, dan parameter -o untuk menentukan direktori kode yang dihasilkan. Setelah menjalankan alat, Anda kini seharusnya memiliki kode yang dihasilkan untuk memanggil API notifikasi berbasis Java dari aplikasi native.

Menerapkan notifikasi native

Di bagian ini, Anda akan mengintegrasikan library notifikasi Android ke dalam aplikasi native menggunakan kode wrapper yang dihasilkan. Langkah pertama adalah mengupdate resource gradle.build level aplikasi (my_project/app/gradle.build) project Anda.

Mengupdate gradle.build

  1. GNI adalah support library yang diperlukan oleh kode wrapper yang dihasilkan. Semua project yang menggunakan kode yang dihasilkan harus mereferensikan library ini. Untuk mereferensikan library ini, tambahkan baris berikut ke bagian dependencies di build.gradle:

    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    
  2. Untuk mengaktifkan dukungan prefab, tambahkan kode berikut ke bagian android:

    buildFeatures {
      prefab true
    }
    
  3. Untuk mengonfigurasi cmake, gunakan konfigurasi cmake berikut di bagian android/defaultConfig:

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

Konfigurasi build.gradle yang telah selesai akan terlihat seperti berikut:

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'
    ...
}

Memodifikasi CMakeLists

  1. Tambahkan library GNI ke CMakeLists.txt (my_project/app/src/main/cpp/CMakeLists.txt) project Anda dengan menambahkan baris berikut di tingkat atas file:

    find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
    
  2. Tambahkan baris berikut ke bagian target_link_libraries:

    PUBLIC com.google.android.gms.gni.c::gni_shared
    
  3. Tambahkan referensi ke kode yang dihasilkan dengan menambahkan baris berikut di tingkat atas file:

    file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
    
  4. Tambahkan baris berikut di dekat akhir file:

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

Resource CMakeLists.txt Anda yang telah diupdate akan menyerupai contoh berikut:

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)

Mengimplementasikan logika notifikasi

  1. Buka atau buat file sumber tempat Anda ingin mengimplementasikan kemampuan notifikasi. Dalam file ini, sertakan file header gni.h dan tentukan fungsi ShowNativeNotification() baru:

    #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. Tentukan nilai konstanta khusus notifikasi, serta CharSequenceFromCString() dan CreateNotification() fungsi pengendali notifikasi:

    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;
    }
    

    Beberapa fungsi library notifikasi menggunakan CharSequence, bukan String. Fungsi CharSequenceFromCString() memungkinkan konversi antar-objek ini. Fungsi CreateNotification() menggunakan NotificationCompat.Builder Java versi gabungan untuk membuat notifikasi.

  3. Tambahkan logika untuk membuat saluran notifikasi dengan menempelkan fungsi berikut, 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. Update fungsi ShowNativeNotification() yang Anda buat sebelumnya untuk memanggil CreateNotificationChannel(). Tambahkan kode berikut ke akhir 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. Setelah menentukan logika, picu notifikasi dengan memanggil ShowNativeNotification() di lokasi yang sesuai dalam project Anda.

Menjalankan aplikasi

Kompilasi dan jalankan kode yang memanggil ShowNativeNotification(). Notifikasi sederhana akan muncul di bagian atas layar perangkat pengujian.

Membuat wrapper dari JAR

Pada contoh sebelumnya, Anda menentukan class dan metode Java secara manual yang memerlukan kode native dalam file konfigurasi wrapper. Untuk skenario saat Anda perlu mengakses sebagian besar API, akan lebih efisien untuk menyediakan satu atau beberapa JAR library ke alat wrapper. Wrapper kemudian menghasilkan wrapper untuk semua simbol publik yang ditemukannya di JAR.

Contoh berikut menggabungkan seluruh Notifications API dengan menyediakan JAR library.

Mendapatkan JAR yang diperlukan

Notification API adalah bagian dari paket androidx.core, yang tersedia dari repositori Google Maven. Download file aar library dan ekstrak ke direktori pilihan Anda. Cari file classes.jar.

File classes.jar berisi banyak class di luar library notifikasi yang diperlukan. Jika Anda hanya memberikan classes.jar wrapper library, alat ini akan menghasilkan kode native untuk setiap class dalam JAR, yang tidak efisien dan tidak diperlukan untuk project kami. Untuk mengatasi hal ini, berikan file filter ke konfigurasi wrapper untuk membatasi pembuatan kode ke class notifikasi JAR.

Menentukan filter izinkan

Filter file adalah file teks biasa yang Anda berikan ke konfigurasi wrapper library. Fungsi ini memungkinkan Anda menentukan class yang akan disertakan (atau dikecualikan) dari file JAR yang disediakan ke wrapper library.

Dalam project Anda, buat file berjudul allowed-symbols.txt dan tempelkan di baris berikut:

androidx.core.app.NotificationCompat*

Saat digunakan sebagai filter izinkan, kode sebelumnya menentukan bahwa hanya simbol yang namanya dimulai dengan androidx.core.app.NotificationCompat yang digabungkan.

Menjalankan wrapper library

Buka terminal ke direktori JAR dan jalankan perintah berikut:

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

Perintah contoh sebelumnya menghasilkan kode wrapper untuk class yang difilter ke generated-jar/ direktori.

Dukungan

Jika Anda menemukan masalah dengan wrapper library, harap beri tahu kami.

Cari bug Laporkan bug
Tim Teknis
Dokumentasi