インターネットで JNI の入門チュートリアルを読んだところ、初心者には非常に不親切で、読みやすい人は混乱し、入門チュートリアルを 0 から 1 まで自分で書くことにしました。
JNIについては、Googleでも入門チュートリアルを提供していますので、詳しくはNDK入門チュートリアルをご確認ください。
1. はじめに
JNI (Java Native Interface) は、Java プログラミング言語によって提供されるプログラミング フレームワークおよびテクノロジであり、Java アプリケーションでネイティブ コード (通常は C/C++ で記述された) を呼び出して、基礎となる関数を実装し、オペレーティング システムおよびハードウェアと対話するために使用されます。JNI を使用すると、開発者は C/C++ などのネイティブ言語でコードを記述し、JNI インターフェイスを通じて Java コードと対話できます。たとえば、一般的なオーディオおよびビデオ処理、画像処理、マップなどでは JNI が使用されます。
2. NDK と CMake
ネイティブ開発キット (NDK) は、Android アプリケーションで C および C++ コードを使用できるようにするツールのセットであり、ネイティブ アクティビティを管理したり、センサーやタッチ エンターなどの物理デバイス コンポーネントにアクセスしたりするために使用できる多数のプラットフォーム ライブラリを提供します。 。
Android Studio 2.2 以降で NDK を使用して C および C++ コードをネイティブ ライブラリにコンパイルし、Android Studio の統合ビルド システム Gradle を使用してネイティブ ライブラリを APK にパッケージ化できます。Java コードは、JNI フレームワークを通じてネイティブ ライブラリ内の関数を呼び出すことができます。
Android Studio がネイティブ ライブラリをコンパイルするためのデフォルトのビルド ツールは CMakeです。既存のプロジェクトの多くは ndk-build ビルド ツールキットを使用しているため、Android Studio も ndk-buildをサポートします。ただし、新しいネイティブ ライブラリを作成する場合は、CMake を使用する必要があります。CMake は、Gradle と連携してネイティブ ライブラリを構築する外部ビルド ツールです。
ndk-build と CMake は両方ともネイティブ コードを構築するためのツールであり、Android Gradle プラグインのバージョン 4.0 以降、Google は CMake の使用を推奨しています。
3. Android.mk、Application.mk、CMakeLists.txt
CMakeLists.txt
および は、Android.mk
Android アプリケーションでネイティブ コードをビルドおよび管理するために使用されるビルド スクリプト ファイルです。
CMakeLists.txt : プロジェクトのビルド プロセスと必要なビルド設定を説明する CMake 構成ファイルです。Android プロジェクトでは、CMakeLists.txt ファイルは通常app
、モジュールのルート ディレクトリにあります。
CMakeLists.txt ファイルには次の内容を含めることができます。
- ビルドする最小の CMake バージョンを定義します。
- 構築するネイティブ ライブラリを宣言します。
- ソースコードファイルを指定します。
- コンパイル オプションを設定し、ライブラリをリンクします。
- 生成された共有ライブラリの名前とプロパティを定義します。
- ビルドの出力パスなどを設定します。
CMake を使用すると、プロジェクトのニーズに応じて C/C++ コンパイラー、ライブラリ パス、コンパイル フラグなどを構成し、ターゲット プラットフォームに適したビルド ファイルを生成できます。その他の使用方法については、「CMake の構成」を参照してください。
Android.mk : Android アプリケーションで使用される Makefile 形式のビルド スクリプト ファイルです。これは、アプリケーションのネイティブ コードを構築および管理するための GNU Make に基づくビルド システムです。デフォルトでは、 のアプリ プロジェクト ディレクトリにあります jni/Android
.mk
。
Android アプリの初期の頃、Android.mk
これは主にネイティブ コードをビルドするために使用される標準のビルド スクリプト ファイルでした。ネイティブ ライブラリとコンパイル設定を記述する方法を提供します。
Android.mk
通常、このファイルは Android プロジェクトのディレクトリにありjni
、次の内容が含まれています。
- ビルドするネイティブ ライブラリの名前を定義します。
- ソースコードファイルを指定します。
- コンパイル オプションを設定し、ライブラリをリンクします。
- 生成された共有ライブラリの名前とプロパティを定義します。
- 共有ライブラリなどの出力パスを指定します。
Android.mk
事前定義された変数と関数、および Makefile 構文を使用してビルド プロセスを制御することもできます。その他の使用方法については、 Android.mkを参照してください。
Application.mk: ndk-build のプロジェクト レベルの設定を指定します。デフォルトでは、 のアプリ プロジェクト ディレクトリにあります jni/Application.mk
。通常、ここではコンパイルされたネイティブ ライブラリの ABI バージョン、つまり armeabi-v7a、arm64-v8a、x86 などを指定します。詳しい構成については、Application.mkを参照してください。
4. データ型
C/C++を使用しているため、データ型の変換に違いがあるはずです。C/C++の基礎知識はご自身で学習してください JNIを使用する前提として、ここではJNIにおけるデータ型変換について説明します。
JNI (Java Native Interface) は、Java コードとネイティブ コード間のデータ転送と型変換のために複数のデータ型をサポートします。一般的な JNI データ型の一部を次に示します。
-
基本的なデータ型:
jboolean
: Java に対応するブール型boolean
。jbyte
: バイト型。Java のバイト型に対応しますbyte
。jchar
: Java に対応する文字タイプchar
。jshort
: Java に対応する短い整数short
。jint
: Java の整数型に対応しますint
。jlong
: Java に対応する長整数long
。jfloat
: Java に対応する単精度浮動小数点型float
。jdouble
:Javaに相当する倍精度浮動小数点型double
。
-
参照タイプ:
jobject
: Java に対応する一般的なオブジェクト参照型Object
。jclass
: Java に対応するクラス参照型Class
。jstring
: Java に対応する文字列型String
。jarray
: Java で配列オブジェクトを表すために使用される配列タイプ。jbooleanArray
: Java に対応するブール配列タイプboolean[]
。jbyteArray
: Java に対応するバイト配列タイプbyte[]
。jcharArray
: Java に対応する文字配列タイプchar[]
。jshortArray
: Java に対応する短い整数配列タイプshort[]
。jintArray
: Java に対応する整数配列タイプint[]
。jlongArray
: Java に対応する長整数配列型long[]
。jfloatArray
: Java に対応する単精度浮動小数点配列型float[]
。jdoubleArray
: Java に対応する倍精度浮動小数点配列型double[]
。
-
他のタイプ:
jthrowable
: Java 例外をスローするために使用される例外タイプ。
JNI では、これらのデータ型は、ネイティブ メソッドのパラメータと戻り値の型を宣言するために使用されます。
V. 定義
1. ファイルの命名:
ndk-build または CMake ツールを使用して生成されるファイルは通常 .so で、命名規則は次のとおりです。
lib+ライブラリ名.so、例: libXXX.so XXX は特定のライブラリの名前を表します。
2. 関数の命名:
JNI では、関数の命名は特定のルールに従って、Java コードとネイティブ コード間の正しいマッピングと相互作用を保証します。JNI 関数の命名規則は、次の形式に基づいています。
Java_package_ClassName_MethodName
Java
: 接頭辞は、これが JNI 関数であることを示します。package
: Java クラスのパッケージ名をドットの代わりにアンダースコアを使用して示します。ClassName
:Javaクラス名を示します。MethodName
:Javaのメソッド名を示します。
JNI 関数の命名規則については、注意が必要な詳細がいくつかあります。
- JNI 関数名のアンダースコアは、
_
さまざまな要素を区切って階層と名前空間の関係を示すために使用されます。 - JNI 関数が静的メソッドの場合は、メソッド名の前にアンダースコアを追加します
_
。 - JNI 関数の場合、戻り値の型とパラメーターの型のシグネチャは Java メソッドのシグネチャと一致する必要があります。シグネチャは、特定の文字を使用してさまざまなタイプを示します。
JNI 関数の命名規則に従う方法を示す例をいくつか示します。
//Java 层代码JNIDemo.java
public class JNIDemo {
static {
System.loadLibrary("libjni");
}
public native String showLog();
}
//Native层代码 jnidemo.cpp
extern "C"
JNIEXPORT jstring JNICALL Java_com_example_jni_JNIDemo_showLog(JNIEnv* env, jobject job) {
return env->NewStringUTF("hello world");
}
上記の例では、showLogcom.example.jni.JNIDemo
という名前のメソッドを含む という名前の Java クラスがあり、ネイティブ層の関数Java_com_example_jni_JNIDemo_showLogがJava 層のパッケージ名 + クラス名 + メソッド名に対応していると仮定します。名前が正しくないと、コンパイル時にエラーが発生します。
3. extern "C" : C 言語と C++ の互換性を示します。
4. JNIEXPORTとJNICALL は、関数の目的を識別するために JNI で定義された2 つのマクロであり、これら 2 つのマクロの定義はシステム環境によって異なります。Android環境では以下のように定義されています
#define JNIIMPORT
#define JNIEXPORT __attribute__ ((visibility ("default")))
#define JNICALL
JNIEXPORT は、関数をエクスポートできるかどうか (関数の可視性) を示すために使用されます。通常の C 言語では、関数や変数の使用を現在のファイルに制限したい場合は、静的な変更を追加する必要があります。ただし、共有ライブラリの指定したファイルに公開したい場合は、シンボルを非表示にしたり表示したりして制御する必要があります。
GCC4.0 以降では、シンボル可視性オプション -fvisibility=vis が提供されます。vis はデフォルト値のdefault、または hidden は非表示を意味します。
対応するコードの可視性属性は、__attribute__((visibility("default"))) または __attribute__((visibility("hidden"))) です。シンボルの出力
形式を簡素化するために、EXPORT を使用してその記述を簡素化できます。
#define EXPORT __attribute__((visibility("default")))
EXPORT int Func();したがって
、JNIEXPORT は #define JNIEXPORT __attribute__((visibility("default"))) と見なすことができます。特定の実装は、コンパイラの違いを判断するために複雑になる場合があります。
JNIEXPORT と JNICALL は Linux 環境では空の定義であるため、Linux ではこれら 2 つのマクロを JNI 関数宣言で省略できます。
5.jstring:関数の戻り値の型。
6. JNIEnv* : これはすべてのネイティブ関数の最初のパラメータであり、JVM 関数テーブルへのポインタです。関数テーブルの各エントリは JNI 関数を指し、各関数は JNI 関数の特定のデータ構造にアクセスするために使用されます。 JVM。
7. jobject : ネイティブ関数を定義する Java クラスまたは Java クラスのインスタンスを表します。
- ネイティブ関数が静的である場合、それはクラス Class オブジェクトを表します。
- ネイティブ関数が静的でない場合、それはクラスのインスタンス オブジェクトを表します。
6. 開発手順
-
Android.mk、Application.mk、または CMakeLists.txt を構成します。
-
ネイティブ コードを作成する: C または C++ でネイティブ コードを作成すると、これらのコードは Java で呼び出したい関数を実装します。
-
JNI インターフェイス ファイルの作成: ネイティブ コードに対応する JNI インターフェイス ファイルを作成します。このファイルは、Java コードとネイティブ コードの間の対話を記述します。
.h格式的头文件
-
JNI メソッドの実装: JNI インターフェイス ファイルで呼び出されるローカル メソッドを定義します。これらのメソッドを実装し、ネイティブ コードに接続する必要があります。
-
.so
ネイティブ ライブラリの生成: ローカル開発ツール (GCC、Clang など) を使用して、ネイティブ コードを共有ライブラリ (ファイルなど)にコンパイルします。 -
System.loadLibrary("your-library-name")
Java でネイティブ ライブラリをロードする:メソッドを使用して、生成されたネイティブ ライブラリをJava コードでロードします。 -
ネイティブ メソッドの呼び出し: Java コードでネイティブ メソッドを宣言し、JNI インターフェイスを介してネイティブ コードを呼び出します。
具体的な実践例は「0から1までのAndroid JNI入門チュートリアル(2) 」で紹介します。