このガイドでは、Android API ライブラリのラッパーの使用方法について説明します。ライブラリ ラッパー コマンドライン ツールは、Java Android API 用の C 言語ラッパーコードを生成します。これにより、Java ライブラリをネイティブ C/C++ Android アプリに統合できます。ライブラリ ラッパーの詳細については、Android API のライブラリ ラッパーをご覧ください。
この手順ガイドでは、ラッパーツールを使用して Java ライブラリをネイティブ Android アプリに統合する方法について説明します。例を示すため、このガイドでは、androidx.core.app
パッケージの通知ライブラリの統合について説明します。このライブラリの詳細については、通知を作成するをご覧ください。
前提条件
このガイドでは、既存のネイティブ Android プロジェクトが存在することを前提としています。また、Gradle ビルドシステムも使用します。既存のプロジェクトが存在しない場合は、Android Studio の Native C++ テンプレートを使用して新しいプロジェクトを作成します。
このガイドのサンプルコードでは、ディレクトリのルート my_project/
を使用しています。ネイティブ コードは、Android Studio プロジェクトのデフォルト ディレクトリ my_project/app/src/main/cpp/
にあります。
ライブラリ ラッパーツールをまだお持ちでない場合は、パッケージをダウンロードして、任意のディレクトリに解凍してください。この CLI ツールには Java Runtime Environment(JRE)が必要です。
ネイティブ コードを生成する
Java ライブラリを統合する場合は、ラッパーツールを使用してネイティブ コードラッパーを生成します。まず、ラッパーを構成します。
ラッパー構成を作成する
ライブラリ ラッパーの構成ファイルを作成して、ネイティブ コード生成ツールの出力を制御します。このファイルの 1 つの機能によって、ラッパーコードを生成するクラスとメソッドを指定できます。
通知ライブラリについては、ラップするための方法が多くは存在しないため、custom_classes
セクションで直接定義できます。メソッドの定義用に、プロジェクトの任意の場所に新しい config.json
リソースを作成します。たとえば、my_project/library_wrapper/config.json
を作成して、次のサンプル構成を貼り付けることができます。
{
"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)"
]
}
]
}
上記のサンプルでは、ネイティブ ラッパーのコードを必要とする Java クラスとメソッドを直接宣言しています。
ライブラリのラッパーを実行する
ラッパー構成ファイルを定義したら、ツールを使用してネイティブ ラッパーコードを生成できます。ライブラリ ラッパーを抽出したターミナルを開き、次のコマンドを実行します。
java -jar lw.jar \
-o "my_project/app/src/main/cpp/native_wrappers" \
-c "my_project/library_wrapper/config.json"
上記のサンプルでは、-c
パラメータを使用してラッパーの構成場所を指定し、-o
パラメータを使用して生成されたコード ディレクトリを定義しています。ツールを実行すると、ネイティブ アプリから Java ベースの Notifications API を呼び出すために必要なコードが生成されます。
ネイティブ通知を実装する
このセクションでは、生成されたラッパーコードを使用して Android 通知ライブラリをネイティブ アプリに統合します。最初のステップとして、プロジェクトのアプリレベルの gradle.build
リソース(my_project/app/gradle.build
)を更新します。
gradle.build
を更新する
GNI は、生成されたラッパーコードに必要なサポート ライブラリです。生成されたコードを使用するすべてのプロジェクトで、このライブラリを参照する必要があります。このライブラリを参照するには、
build.gradle
のdependencies
セクションに次の行を追加します。implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
prefab のサポートを有効にするには、
android
セクションに次のコードを追加します。buildFeatures { prefab true }
cmake
を構成するには、android/defaultConfig
セクションで次のcmake
構成を使用します。externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_shared' } }
完成した build.gradle
構成は次のようになります。
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'
...
}
CMakeLists
を変更する
ファイルの最上位に次の行を追加して、GNI ライブラリをプロジェクトの
CMakeLists.txt
(my_project/app/src/main/cpp/CMakeLists.txt
)に追加します。find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
target_link_libraries
セクションに次の行を追加します。PUBLIC com.google.android.gms.gni.c::gni_shared
ファイルの最上位に次の行を追加して、生成されたコードへの参照を追加します。
file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
次の行をファイルの末尾付近に追加します。
include_directories(./native_wrappers/c) include_directories(./native_wrappers/cpp)
更新された CMakeLists.txt
リソースは、次のサンプルのようになります。
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)
通知ロジックを実装する
通知機能を実装するソースファイルを開くか、作成します。このファイルにヘッダー ファイル
gni.h
を追加し、新しい関数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); }
通知固有の定数値と、通知ハンドラ関数
CharSequenceFromCString()
と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; }
通知ライブラリの一部の関数は、
String
ではなくCharSequence
を受け取ります。CharSequenceFromCString()
関数を使用すると、これらのオブジェクト間での変換が可能になります。関数CreateNotification()
は、JavaNotificationCompat.Builder
のラップされたバージョンを使用して通知を作成します。次の関数
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); }
先ほど作成した
ShowNativeNotification()
関数を更新して、CreateNotificationChannel()
を呼び出します。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(¬ification); NotificationManagerCompat::destroy(¬ification_manager); }
ロジックを定義して、プロジェクトの適切な場所で
ShowNativeNotification()
を呼び出して通知をトリガーします。
アプリを実行する
ShowNativeNotification()
を呼び出すコードをコンパイルして実行します。テストデバイスの画面の上部に単純な通知が表示されます。
JAR からラッパーを生成する
前の例では、ラッパー構成ファイルにネイティブ コードを必要とする Java クラスとメソッドを手動で定義しました。API の大規模なセクションにアクセスする必要があるシナリオでは、ラッパーツールに 1 つ以上のライブラリ JAR を指定するほうが効率的です。このようにするとラッパーは、JAR で検出されたすべての公開シンボルのラッパーを生成します。
次の例では、ライブラリ JAR を指定して Notifications API 全体をラップします。
必要な JAR を取得する
Notification API は、androidx.core
パッケージの一部であり、Google Maven リポジトリから入手できます。ライブラリ aar ファイルをダウンロードして、任意のディレクトリに展開します。classes.jar
ファイルを見つけます。
classes.jar
ファイルには、必要な通知ライブラリ以外のクラスが多数含まれています。ライブラリ ラッパーに classes.jar
のみを指定すると、このツールは JAR 内のすべてのクラスに対してネイティブ コードを生成するため、これは効率が低くプロジェクトで必要のない処理です。この問題を解決するには、ラッパー構成にフィルタ ファイルを指定して、コード生成を JAR の通知クラスに制限します。
許可フィルタを定義する
フィルタ ファイルは、ライブラリ ラッパーの構成に対して指定する書式なしテキスト ファイルです。ライブラリ ラッパーに指定した JAR ファイルに含める(または除外する)クラスを定義できます。
プロジェクトで allowed-symbols.txt
という名前のファイルを作成して、次の行に貼り付けます。
androidx.core.app.NotificationCompat*
上記のコードで許可フィルタとして使用すると、名前が androidx.core.app.NotificationCompat
で始まるシンボルのみがラップされます。
ライブラリのラッパーを実行する
JAR ディレクトリのターミナルを開いて、次のコマンドを実行します。
java -jar lw.jar \
-i classes.jar \
-o "./generated-jar" \
-c "./config.json" \
-fa allowed-symbols.txt \
--skip_deprecated_symbols
上記のサンプル コマンドは、フィルタリングされたクラスのラッパーコードをディレクトリ generated-jar/
に生成します。
サポート
ライブラリのラッパーに問題がある場合は、お知らせください。
バグを参照する | バグを報告する |
---|---|
エンジニアリング | bug_report |
ドキュメント | bug_report |