NDK でビルドしたミドルウェアを配布する場合、アプリ デベロッパーが気にする必要のない問題が発生します。ビルド済みライブラリでは、ユーザーが実装の選択を行う場合があります。
API レベルと NDK バージョンの選択
ユーザーは、ミドルウェアより低い minSdkVersion を使用できません。たとえばユーザーのアプリを API 21 で実行する必要がある場合、API 24 向けにビルドすることはできません。ユーザーより低い API レベル向けにライブラリをビルドするのは問題ありません。たとえば API 16 向けにビルドしても、API 21 ユーザーと互換性を維持できます。
NDK バージョンは相互にほぼ互換性がありますが、互換性を損なう変更が行われることもあります。すべてのユーザーが同じバージョンの NDK を使用していることがわかっている場合は、そのバージョンと同じバージョンを使用することをおすすめします。それ以外の場合は、最新バージョンを使用してください。
STL の使用
C++ で記述するときに STL を使用していて、共有ライブラリを配布する場合は、libc++_shared
か libc++_static
かの選択がユーザーに影響を与えます。共有ライブラリを配布する場合は、libc++_shared
を使用するか、libc++ のシンボルがライブラリによって公開されないようにする必要があります。そのためには、バージョン スクリプトを使用して ABI サーフェスを明示的に宣言します(これにより、実装の詳細を非公開のままにすることもできます)。たとえば、簡単な演算ライブラリに次のようなバージョン スクリプトを含めることができます。
LIBMYMATH {
global:
add;
sub;
mul;
div;
# C++ symbols in an extern block will be mangled automatically. See
# https://stackoverflow.com/a/21845178/632035 for more examples.
extern "C++" {
"pow(int, int)";
}
local:
*;
};
バージョン スクリプトはシンボルの表示を制御する最も堅牢な方法であるため、ご使用になることをおすすめします。この方法は、実装の詳細が公開されるのを防いで読み込み時間を改善するため、ミドルウェアかどうかを問わず、すべての共有ライブラリでのおすすめの方法です。
また、堅牢性は劣りますが、リンク時に -Wl,--exclude-libs,libc++_static.a
-Wl,--exclude-libs,libc++abi.a
を使用する方法もあります。この方法では、明示的に指定されていないライブラリのシンボルのみが非表示になり、使用されないライブラリの診断は報告されないため、堅牢性が低くなります(ライブラリ名をタイプミスしてもエラーにはなりません。また、ライブラリ リストを最新の状態に保つ責任はユーザーにあります)。また、この方法では自身の実装の詳細が非表示になりません。
AAR でのネイティブ ライブラリの配布
Android Gradle プラグインは、AAR で配布されたネイティブ依存関係を読み込むことができます。ユーザーが Android Gradle プラグインを使用している場合は、これがライブラリを利用するための最も簡単な方法です。
ネイティブ ライブラリは、AGP によって AAR にパッケージ化できます。ライブラリがすでに externalNativeBuild でビルドされている場合は、これが最も簡単な方法です。
AGP 以外のビルドでは、ndkports を使用できます。または、Prefab ドキュメントに沿って、手動によるパッケージ化を実行して AAR の prefab/
サブディレクトリを作成することもできます。
JNI ライブラリを含む Java ミドルウェア
JNI ライブラリを含む Java ライブラリ(つまり jniLibs
を含む AAR)では、ライブラリ内の JNI ライブラリが、ユーザーのアプリ内の他のライブラリと競合しないように注意する必要があります。たとえば、AAR に libc++_shared.so
が含まれ、アプリが使用するものとは異なるバージョンの libc++_shared.so
である場合、APK にインストールされるバージョンは 1 つのみであるため、信頼できない動作につながる可能性があります。
最も信頼性の高いソリューションは、Java ライブラリに含める JNI ライブラリを 1 つだけにすることです(これはアプリについても同様です)。STL を含むすべての依存関係は実装ライブラリに静的にリンクされ、バージョン スクリプトを使用して ABI サーフェスを実行する必要があります。たとえば、JNI ライブラリ libfooimpl.so
を含む Java ライブラリ com.example.foo
は、次のバージョン スクリプトを使用する必要があります。
LIBFOOIMPL {
global:
JNI_OnLoad;
local:
*;
};
この例では、JNI のヒントに記載のとおり JNI_OnLoad
を介して registerNatives
を使用し、最小限の ABI サーフェスが公開され、ライブラリの読み込み時間が最小限になるようにしています。