前回の「0から1までのAndroid JNI入門チュートリアル(1)」でJNIについての予備的な理解ができたので、次にndk-buildメソッドとcmakeメソッドからネイティブライブラリを構築する方法を紹介します。
この記事では、次の開発環境を使用します。
Android Studio 4.0以降
コンパイルSDK 33
グラドル7.2.1
1.ndk-ビルド
ndk-build は設定ファイル Android.mk に依存しており、コードが保存される場所は通常 jni ディレクトリです。
1. 新しい Java テスト クラスを作成する
package com.example.jni;
public class JNIDemo {
static {
//这个库名必须跟Android.mk的LOCAL_MODULE对应
System.loadLibrary("JniDemo");
}
public static native String test();
}
2. jni ディレクトリを作成します。
3. .h ファイルを生成する
コマンド ラインを開き、プロジェクトの Java ディレクトリに切り替え、次のコマンドを使用して .h ヘッダー ファイルを生成します。(javahコマンドがない場合は、まずjdk環境変数を設定してください)
javah -d ../jni com.example.jni.JNIDemo
生成されたファイルは jni ディレクトリに表示されます。
4. 特定の C++ 関数を実装する
jni ディレクトリに次の内容の新しい .cpp ファイルを作成します。
#include "com_example_jni_JNIDemo.h" //引入刚刚生成的头文件
extern "C"
//实现test函数实际功能
JNIEXPORT jstring JNICALL Java_com_example_jni_JNIDemo_test(JNIEnv * env, jclass clz){
return env->NewStringUTF("hello world");
}
5. Android.mk と Application.mk を構成する
これら 2 つのファイルを jni ディレクトリに作成します。ファイルの内容は次のとおりです。
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#定义要构建生成的本地库的名称,以下示例会生成名为 libJniDemo.so 的库。
LOCAL_MODULE := JniDemo
#指定源代码文件
LOCAL_SRC_FILES := \
JNITest.cpp \
include $(BUILD_SHARED_LIBRARY)
アプリケーション.mk
#指定用于此应用的 C++ 标准库,默认情况下使用 system STL。其他选项包括 c++_shared、c++_static 和 none
APP_STL := c++_shared
APP_CPPFLAGS := -frtti -fexceptions
# APP_ABI:指定生成哪些cpu类型的so, all代表全部 常用的有:armeabi-v7a,arm64-v8a,x86,x86_64
APP_ABI := armeabi-v7a arm64-v8a
#APP_PLATFORM 会声明构建此应用所面向的 Android API 级别,并对应于应用的 minSdkVersion
APP_PLATFORM := android-21
注: Gradle は externalNativeBuild
無視します APP_ABI
。ndkabiFilters
ブロック内の ブロックまたはチャンクを使用してください (abi
以下の構成を参照)。
6. .so ファイルを生成する
ndk-build コマンドを直接使用してビルドすることも、AndroidStudio でショートカットを構成することもできます。(この例では ndk バージョン 21.4.7075529 を使用します)
(1) build コマンドで
ビルドする コンソールを jni ディレクトリ、つまり Android.mk と Application.mk が含まれるディレクトリに切り替えて、ndk-build コマンドを実行すると、成功すると libs フォルダー内にビルドが見つかります。(ndk-build コマンドがない場合は、最初に ndk 環境変数を設定する必要があります)
(2) ショートカットツールの設定
[ファイル]、[設定]、[ツール]、[外部ツール] の順に開き、新しいツールを追加し、ndk-build という名前を付けます (任意の名前を付けます)。プログラム構成で ndk が配置されているディレクトリで ndk-build.cmd を選択します。 AndroidStudio にインストールされている ndk ディレクトリの下にある作業ディレクトリは、プロジェクトの jni ディレクトリになります。
クリックして ndk-build を実行し、.so ファイルを生成します。
7. コード内でネイティブ メソッドを使用する
public class MainActivity extends AppCompatActivity {
private TextView tvMsg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvMsg = findViewById(R.id.tv_msg);
//调用native方法
String msg = JNIDemo.test();
tvMsg.setText(msg);
}
}
古いバージョンの Gradle を使用している場合は、アプリ モジュールの build.gradle に次の構成を追加して、so ライブラリをリンクする必要があります (生成された so ライブラリは libs ディレクトリに配置する必要があります)。
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
//...其他省略
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
//...其他省略
}
2.CMake
上記では ndk-build について説明しました。次に、CMake の構成方法について説明します。
1. CMakeLists.txt ファイルを追加します。
プロジェクトのアプリ モジュールのルート ディレクトリに新しい CMakeLists.txt ファイルを作成し、次の内容を入力します。
#指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.18.1)
# 定义本地库的名称
set(my_lib_name JniDemo)
#添加库配置,如果有多个库,可以添加多个add_library方法
add_library( # 指定生成库的名称,使用上面定义的变量
${my_lib_name}
# 标识是静态库还是动态库 STATIC:静态库 SHARED:动态库
SHARED
# C/C++源代码文件路径
src/main/cpp/JNITest.cpp)
#指定.h头文件的目录
include_directories(src/main/cpp/)
# 指定构建输出路径
set_target_properties(${my_lib_name} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")
# 链接外部静态库(如果你的静态库或动态库依赖于其他库或依赖项)
target_link_libraries(MyStaticLibrary PRIVATE MyExternalStaticLibrary)
例の関数はadd_library
、ビルドするネイティブ ライブラリとそのソース コード ファイルを指定するために使用されます。この関数は、Android のロギング機能をネイティブ コードで使用するために使用されるライブラリtarget_link_libraries
など、リンクするシステム ライブラリを指定するために使用されます。log
最後に、この関数を使用して、生成された共有ライブラリの出力パスを指定し、ディレクトリset_target_properties
に配置できます。これは、現在のターゲット プラットフォームの ABI (アプリケーション バイナリ インターフェイス) を表す CMake 変数です。libs/${ANDROID_ABI}
${ANDROID_ABI}
CMakeLists.txt のその他の設定項目については、Cmake の設定を参照してください。
2.アプリモジュールの下のbuild.gradleにexternalNativeBuildとndk構成を追加します。
android {
compileSdk 33
defaultConfig {
applicationId "com.example.jni"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
//指定编译的ABI平台
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
externalNativeBuild {
cmake {
//指定CMakeLists文件所在位置
path "CMakeLists.txt"
}
}
//...省略
}
3. C++ ファイルを追加する
CMake は通常、jni ディレクトリではなく cpp ディレクトリを使用するため、src/main ディレクトリの下に新しい cpp ディレクトリを作成し、そこに .h および .cpp ファイルを置きます。
4. .so ファイルを生成する
AS の Rebuild Project を使用してプロジェクトをビルドします。つまり、対応する so ファイルを libs ディレクトリに生成できます。
さて、ここまでで2つの方法の紹介は終わりです。
設定の詳細については、Google の公式ドキュメントを参照してください。
追記
上記のプロセスでは、動的ライブラリと静的ライブラリという 2 つの概念が関係しました。
NDK コンパイル プロセスでは、これら 2 つの異なるライブラリ ファイル生成方法が使用されます。
1. ダイナミック ライブラリ (ダイナミック ライブラリ):
- ダイナミック ライブラリは、実行時にアプリケーションにロードされてリンクされるライブラリ ファイルです。Android NDK では、ダイナミック ライブラリのファイル拡張子は通常
.so
(Shared Object) です。 - 動的ライブラリは、複数のアプリケーションまたはモジュールで共有して、ディスク容量とメモリ使用量を削減できます。複数のアプリケーションが同じ動的ライブラリを同時にロードして使用できます。
- ダイナミック ライブラリのロードとリンクは実行時に実行されます。つまり、アプリケーションの実行中にライブラリ ファイルを動的にロードして置き換えることができるため、更新とメンテナンスの利便性が実現されます。
- Android NDK では、C/C++ コンパイラーを使用して動的ライブラリが生成され、
ndk-build
CMake や CMake などのビルド ツールを使用してコンパイルおよびビルドされます。
2. 静的ライブラリ:
- 静的ライブラリは、コンパイル時にアプリケーションにリンクされるライブラリ ファイルです。Android NDK では、静的ライブラリのファイル拡張子は通常
.a
(Archive) です。 - 静的ライブラリはアプリケーションのコンパイル時に実行可能ファイルにコピーされ、静的ライブラリを使用するすべてのアプリケーションにはライブラリの完全なコピーが含まれます。
- 静的ライブラリの利点は、外部ライブラリ ファイルに依存せずに独立した実行可能ファイルを提供できることです。静的ライブラリの関数呼び出しはダイナミック リンク プロセスを介さずに直接実行されるため、パフォーマンスではダイナミック ライブラリよりわずかに優れている可能性があります。
- Android NDK では、静的ライブラリも C/C++ コンパイラを使用して生成され、
ndk-build
CMake や CMake などのビルド ツールを使用してコンパイルおよびビルドされます。
Android NDK 開発では、ニーズに応じて動的ライブラリまたは静的ライブラリの使用を選択できます。動的ライブラリは、更新とメンテナンスを容易にするために複数のアプリケーションがライブラリ ファイルを共有するシナリオに適していますが、静的ライブラリは、独立した実行可能ファイル、パフォーマンスの最適化が必要で、外部ライブラリに依存する必要がないシナリオに適しています。