ネイティブ
NDK
AndroidSDKとNDKを使用していC/C++で開発したネイティブモジュールを同梱させる方法がある
NDKとは
apkファイルにC/C++で開発したネイティブモジュールを同梱できる。
NDK(Natie Development Kit)とはネイティブモジュールをビルドするための開発ツール。
メリット
- 新たに開発したネイティブモジュールをAndroidアプリケーションに組み込める
- 既存資産でのネイティブモジュールをAndroidアプリケーションに組み込める
- パフォーマンスの改善が見込まれるため、負荷の高い(ゲーム開発など)で利用できる
デメリット
- NDK単独ではアプリケーションの開発が出来ない
- ネイティブモジュールはJavaで開発されたアプリケーションの1モジュールなので他のアプリケーションでは利用できない
- ネイティブとJavaのやり取りはJNIを使用するため、JNIによるオーバヘッドを考慮する必要がある。そのため必ず改善するとは限らない
- 実行されるターゲット端末のCPUごとにネイティブモジュールを作る必要がある
- 使用できるC/C++のライブラリに制限がある。
開発環境
ネイティブモジュールの開発環境
項目 | 必要な環境 |
---|---|
OS | Windows XP, Windows Vista以降(+cygwin), Mac OSX, Linux |
Java JDK | JDK6以降 |
Android SDK | Android SDK 1.5以上 |
ツール | Cygwin: gmake, gcc, Linux: GNU Make, GNU AwkまたはNawk |
NDKの開発コンポーネント
ビルドツール
ネイティブモジュールのビルドを簡単にすることが出来るツール類(シェルスクリプト)
NDKの解凍フォルダの/buildフォルダ配下に含まれている。
通常ビルドに使うのは解凍フォルダ直下にあるndk-buildコマンド。
デバッグツール
NDKにはgdbserver1、gdbが含まれており、使用してデバッグが出来る
gdbでデバックできるようにするにはndk-buildでビルドする際に-B NDK_DEBUG=1オプションをつけてビルドし、またアプリケーションのAndroidManifest.xmlでdebuggable="true"を指定しておく。
デバッグ対象のアプリケーションがエミュレータか実機で起動したあと、コマンドラインからndk-gdbコマンドで起動する
クロスコンパイラ
ネイティブモジュールモジュールはターゲットボードに搭載されているCPUアーキテクチャ用のコンパイラでコンパイルしてモジュールを作る必要がある。Android NDK r8ではARM(ARMv5TE, ARM-v7a), MIPS, X86のクロスコンパイラが提供されている。コンパイルオプション(Android.mkファイルの記述)で指定できる
C/C++ライブラリ群
開発に必要なC/C++のライブラリが含まれている
Cはlibc(標準ライブラリ), libm(算術ライブラリ), JNI(Java Native Interface), libz(データの圧縮および伸張機能ライブラリ),liblog(Androidのロギングライブラリ)など
C++はlibstd++が使用できる。コンパイルオプションによってSTLPortベースのC++STLや例外、RTTIがサポートされる
他にOpenGL/ES(3D描画エンジンライブラリ), OpenSL/ES(オーディオライブラリ), OpenMAX(組み込み機器向け汎用ストリーミングメディアライブラリ)、他にAndroidのネイティブアプリケーション用のAPI(ネイティブによるアクティビティ作成や入出力のサブシステム、センサデータアクセス)がサポートされる。
NDKの使い方
NDKを使ってネイティブモジュールを同梱したアプリケーションを作成する手順
- Eclipseなどを使ってAndroidアプリプロジェクトを作る
- プロジェクトのパス直下にjniフォルダを作成し、その下にAndroid.mkファイルを作成する
- 必要であればApplication.mkファイルを作成する
- ネイティブコード(C, C++)はjniフォルダ配下に格納する
- Android.mkにビルドに必要な設定を記述するLOCAL_MODULEいは作成するネイティブモジュール名, LOCAL_SRC_FILESにはビルド対象のソースコードを設定。
- ndk-buildコマンドを実行してネイティブコードをコンパイルしネイティブモジュールをビルド。
- 成功すると、プロジェクトのlibs/eabiフォルダが作成される。その中にlib.soが作成される。
- Eclipse上でビルドをして、apkファイルを作成する。
Android.mk
ネイティブモジュールを作成するのに必要な設定はこのファイルに設定する。
Android.mkの例
LOCAL_PATH := $(call my-dir)
include $(CREAR_VARS)
LOCAL_MODULE := hello-ndk
LOCAL_SRC_MODULE := hello-ndk.c
include $(BUILD_SHARED_LIBRARY)
1行目、my-dirはマクロ変数でAndroid.mkファイルの存在するカレントフォルダを指している。ファイルの先頭で必ず設定すること
2行目、変数のクリアを実施する
3行目、ネイティブモジュール名を指定(ファイル名はlibhello-ndk.soのようになる)
4行目、ソースコードファイル名を指定する。複数の場合はスペースで区切る
5行目、共有ライブラリ(.so)を生成する設定をする。BUILD_STATIC_LIBRARYを設定した場合はスタティックライブラリ(.a)の生成が定義できる
Android.mkのその他のモジュール詳細記述変数
変数 | 説明 |
---|---|
TARGET_ARCH | ターゲットとするアーキテクチャを指定"arm" |
TARGET_PLATFORM | ターゲットとするAndroidプラットフォームを指定"android" |
TARGET_ARCH_ABI | ターゲットとするARMのABIを指定"armeabi":Armv5TE用、"armeabi-v7a":Armv7用 |
TARGET_ABI | $(TARGET_PLATFORM) -$(TARGET_ARCH_ABI)と同義。"android--armeabi" |
LOCAL_CPP_EXTENSION | C++ファイルの拡張子を設定。".cxx" |
LOCAL_C_INCLUDES | includeファイルのパスを設定 |
LOCAL_CFLAGS | Cのコンパイルオプションの設定 |
LOCAL_CXXFLAGS(CPPFLAGS) | C++のコンパイルオプションの設定 |
LOCAL_STATIC_LIBRARIES | LOCAL_MODULEとリンクするスタティックライブラリを指定 |
LOCAL_SHARED_LIBRARIES | LOCAL_MODULEとリンクする共有ライブラリを設定 |
LOCAL_LDLIBS | リンカに渡すオプション |
LOCAL_ARM_NEON | NEON命令の利用を許可するか指定 |
Application.mkとは
アプリケーションで必要なネイティブモジュールの設定を行うファイル。設定しないオプションがなければ必須ではない。
最小構成としては、APP_PROJECT_PATHを指定するのみ。アプリケーションのルートディレクトリの絶対パスを指定する。
Application.mkでオプション設定をするための変数
変数 | 説明 |
---|---|
APP_MODULES | モジュール名の設定(スペース区切りでリスト記述可能) |
APP_ABI | ABIの設定 |
APP_PLATFORM | アプリケーションプラットフォームの設定 |
JNI
JNIとは
JNI(Java Native Interface)はJavaとネイティブコードを連携する仕組みである。
JNIの仕組み
ネイティブコードはjni.hで定義されたJNI関数を呼び出してJava仮想マシン機能にアクセスする。
JNIEnv型のJNIインターフェースポインタを用いる。
JNIのインターフェース規約
ネイティブコードがJava仮想マシン機能にアクセスできるように、インターフェース規約が定められている
JNIの型の規約
Javaのプリミティブ型や参照型はネイティブコードでは別名で定義されている。
Javaの型 | Javaのシグニチャの型 | JNIの型 | 備考 |
---|---|---|---|
Boolean | Z | jboolean | 符号なし8ビット |
byte | B | jbyte | 符号付き8ビット |
char | C | jchar | 符号なし16ビット |
short | S | jshort | 符号付き16ビット |
int | I | jint | 符号なし32ビット |
long | J | jlong | 符号付き32ビット |
float | F | jfloat | 32ビット |
double | D | jdouble | 64ビット |
Objectおよびそのサブクラス | Ljava/lang/Object; | jobject | |
java.lang.Class | Ljavalang/Class; | jclass | |
java.lang.String | Ljava/lang/String; | jstring | |
Object[] | [Ljava/lang/Object; | jobjectArray | |
boolean[] | [Z | jbooleanArray | |
byte[] | [B | jbyteArray | |
char[] | [C | jcharArray | |
short[] | [S | jshortArray | |
int[] | [I | jintArray | |
long[] | [J | jlongArray | |
float[] | [F | jfloatArray | |
double[] | [D | jdoubleArray | |
void | V |
Javaのシグニチャとは、メソッド名、引数の個数、各引数の型の組み合わせのこと。Javaのクラスで定義したメソッドのシグニチャはjavap -s コマンドで確認できる
JNIのメソッド命名規約
JavaのクラスからネイティブのJNIメソッドを呼び出す場合はnaitive修飾子を用いてネイティブのJNIメソッドを宣言する
呼び出されるネイティブメソッドは、Java_<呼び出し元Javaのクラスの完全修飾名を.でなく_でつないだもの>_<ネイティブメソッド名>となる。ex. Java_ace_sample_jni_SampleJni_getName(){}
Javaのクラスで宣言したネイティブメソッド名はJDKのjavahツールでヘッダファイルを作成することで確認できる
JNIのメソッド引数
JNIのネイティブメソッドの第一引数と第二引数は決まっている。
第一引数はJNIEnv型のポインタ、第二引数はjobject型(呼び出し元Javaクラスのインスタンス参照値)またはjclass型(呼び出し元のJavaクラスのclassオブジェクト)が渡される。
ネイティブメソッドの引数がある場合は第三引数以降に記述する。
JNI関数
JNIEnv型のポインタで使用できるJNI関数の一部
JNI関数 | 説明 | 引数 |
---|---|---|
jstring NewStringUTF(JNIEnv *env, const char *bytes:) | UTF-8にエンコードした文字列の参照ポインタからjava.lang.Stringオブジェクトを生成 | |
void REleaseStringUTFChars(JINEnv env, jstring string, char bytes): | 確保した文字列配列のメモリを解放 | |
jintArray NewIntArray(JNIEnv *env, jsize length): | int配列を生成 | |
void ReleaseIntArrayElements(JNIEnv env, jinitArray array, jint elems, jint mode): | 確保したint配列のメモリを開放する | |
jclass FindClass(JNIEnv *env, const char *name): | クラス名からjclassを取得 | |
jclass GetObjectClass(JNIEnv *env, jobject obj): | Java classオブジェクト取得 | |
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig): | メソッドのIDを取得 | |
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) | StaticメソッドのIDを取得 | |
NativeType Call<type> Method(JNIEnv *env, jobject obj, jmethodID methodID, ...: |
メソッドIDが示すJavaのメソッドを呼び出す。<type> はメソッドの戻り値の型名。NativeTypeはメソッド戻り値の型のJNIの別名定義の型 |
|
NativeType CallStatic<type> Method(JNEnv *env, jclass clazz, jmethodID methodID, ...): |
メソッドIDが示すJavaのstaticメソッドを呼び出す。<type> はメソッドの戻り値の型名。NativeTypeはメソッド戻り値の型のJNIの別名定義の型 |
ネイティブコードではメモリ管理を自分で行う必要がある。使用後は明示的に開放すること。
仮想マシン(VM)の参照
JNIEnv型のポインタはネイティブメソッドの呼び出しの間だけ有効。呼び出し以外のネイティブコードでJNIEnv型のポインタを使いたい場合は、何らかの方法で取得する必要がある。
通常のJavaの場合はJINI_CreateJavaVMメソッドを使用することでできるが、AndroidのDalvikVMではできない。
スレッドを生成し、その中でAttachCurrentThreadメソッドを使用することで取得する。不要になったら明示的にDetachCurrentThreadメソッドを呼び出して切断する。すでにVMに接続済みであればGetEnvメソッドで取得できる。
AttatchCurrentThreadメソッドによる接続には、JavaVMの参照ポインタが必要となる。参照ポインタはJNI_OnLoadメソッドで取得しておく。このメソッドはJavaでSystem.loadLivraryが実行されるタイミングで呼ばれる関数である。
JNI_OnLoadとJNI_OnUnLoad
JNI_OnLoadはオプションで提供されている関数。ネイティブライブラリ内に記述しておくことで、Java側でSystem.loadLibraryが実行された際に呼ばれるのでネイティブの初期化処理をするのに適している。
JNI_OnUnLoadも同様で、ガベージコレクションされる際にVMから呼ばれるので、メモリの開放やVMとの切断処理などを実装するのに適している。
ASE
ASEとは
ASE(Android Scripting Environment)とはAndroid端末上でスクリプト言語の編集や実行を可能にする環境。
SL4A(Scripting Layer for Android)というものが開発されている。
ASEでサポートされているスクリプト言語(2012年5月時点)
- BeanShell
- JRuby
- Lua
- PHP
- Perl
- Python
- Rhino(JavaScriptのインタプリタ)
- シェルスクリプト(bsh)
ASEでサポートされているスクリプト言語からはAndroidフレームワークのAPI(Intentの利用やActivityの起動、電話やSMSの発信、バーコードのスキャン、位置情報やセンサデータの取得)にアクセスできる。