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
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
dibuild.gradle
:implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
Untuk mengaktifkan dukungan prefab, tambahkan kode berikut ke bagian
android
:buildFeatures { prefab true }
Untuk mengonfigurasi
cmake
, gunakan konfigurasicmake
berikut di bagianandroid/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
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)
Tambahkan baris berikut ke bagian
target_link_libraries
:PUBLIC com.google.android.gms.gni.c::gni_shared
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")
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
Buka atau buat file sumber tempat Anda ingin mengimplementasikan kemampuan notifikasi. Dalam file ini, sertakan file header
gni.h
dan tentukan fungsiShowNativeNotification()
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); }
Tentukan nilai konstanta khusus notifikasi, serta
CharSequenceFromCString()
danCreateNotification()
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
, bukanString
. FungsiCharSequenceFromCString()
memungkinkan konversi antar-objek ini. FungsiCreateNotification()
menggunakanNotificationCompat.Builder
Java versi gabungan untuk membuat notifikasi.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(¬ification_manager_as_object); NotificationManager::destroy(notification_manager); }
Update fungsi
ShowNativeNotification()
yang Anda buat sebelumnya untuk memanggilCreateNotificationChannel()
. Tambahkan kode berikut ke akhirShowNativeNotification()
: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); }
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 | bug_report |
Dokumentasi | bug_report |