2009年09月04日
Android NDKの一歩進んだ使い方
こちらのエントリーとこちらのエントリーで得られた、本気でAndroid NDKを使う場合に、引っかかるであろうポイントを一挙公開です。
基本事項に関しては、こちらの木南さんによる解説を参考にしてください。
■Android NDK用 make file編
Android NDKのmake fileというべき、
Android.mk
についてのテクニック紹介編です。
ファイル自体は、下記にあります。
/android-ndk-x.x/sources/[プロジェクト名]/
●includeファイルの登録
・自分がいるディレクトリを変数に入れる
下記のようにして、$(LOCAL_PATH)にいまいるディレクトリが登録します。
LOCAL_PATH:= $(call my-dir)
・includeファイルの登録
$(LOCAL_PATH)を基準に、「../」などを駆使して、目的のincludeファイルのあるディレクトリを指定します。
ただし、Android NDKからは、LOCAL_C_INCLUDESが呼び出せないようなので、下記のような小細工をします。
LOCAL_C_INCLUDES := $(LOCAL_PATH)/cv/src $(LOCAL_PATH)/cv/include
LOCAL_CFLAGS += $(LOCAL_C_INCLUDES:%=-I%)
●ldの登録
現在のAndroid NDKでは、バグがあるようで、Android.mkにおいて下記のライブラリをロードしておかないとエラーとなります。
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl
●階層つきディレクトリのコンパイル
「includeファイルの登録」と基本的には、同じです。
$(LOCAL_PATH)を基準に、「../」などを駆使して、目的のソースファイルを指定します。
LOCAL_SRC_FILES := cxcore/src/WLNonFileByteStream.cpp cv/src/cvjni.cpp
●複数ライブラリの統合
staticライブラリであれば、いくつでも追加可能です。
・Application.mk のモジュールへの複数登録
/android-ndk-x.x/apps/[プロジェクト名]/ にある、Application.mk の APP_MODULES に統合したい分だけ、モジュール名を列挙します。
モジュール名とは、Android.mkで「LOCAL_MODULE := opencv」と宣言しているモノです。
APP_MODULES := cxcore cv cvaux cvml cvhighgui opencv
・staticライブラリをコンパイルする
staticライブラリをソースから同時に作成した場合は、「Android.mk」内で「include $(BUILD_STATIC_LIBRARY)」と宣言することにより、作成可能です。
※もちろん、統合だけであれば、作成済みのモノでもOKです。
LOCAL_MODULE := cxcore
LOCAL_SRC_FILES := cxcore/src/cxalloc.cpp cxcore/src/cxarithm.cpp
include $(BUILD_STATIC_LIBRARY)
・出力先 out
上記のコンパイルされたオブジェクトやstaticライブラリは、下記の出力されます。
/android-ndk-x.x/out/apps/[プロジェクト名]/
・staticライブラリの統合
「LOCAL_STATIC_LIBRARIES」に、「include $(BUILD_STATIC_LIBRARY)」して作ったstaticライブラリのモジュール名を登録しただけでは、staticライブラリ内から他のstaticライブラリを呼ぶコードがある場合、エラーとなってしまいます。
そこで、「LOCAL_LDLIBS」に、「出力先 out」などを参考に必要なstaticライブラリを登録してください。
LOCAL_MODULE := opencv※リンクするライブラリのヘッダファイルをincludeすることも忘れないようにしましょう。
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl -llog -L$(LOCAL_PATH)/../../out/apps/opencv/android-1.5-arm/ -lcxcore -lcv -lcvaux
LOCAL_SRC_FILES := cvjni.cpp
LOCAL_STATIC_LIBRARIES := cxcore cv cvaux cvml cvhighgui
include $(BUILD_SHARED_LIBRARY)
■Android NDK 開発編
●extern "C"
C++と混ぜるなら、JNI用の関数に対しては、「extern "C"」が必要です。
そのため、プロトタイプ宣言で下記のようにすると良いようです。
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT
jbooleanArray
JNICALL
Java_org_siprop_opencv_OpenCV_findContours(JNIEnv* env, jobject thiz, jintArray photo_data, jint width, jint height);
#ifdef __cplusplus
}
#endif
●ファイルの読み書き
Android NDK(C言語)側とAndroid側でファイルを共有するには、下記のディレクトリを利用するのがよいようです。
ここにあるものは、FILE系関数で読み書き可能となります。
/data/data/[パッケージ名]/files/
「/tmp」なども使えますが、ここを使うとAndroid側のContext#openFileOutput(String)と連携しやすいので、ここを使うことをお奨めします。
●高度なデバッグ方法
組み込みプレスVol.16によくまとまっておりますので、こちらを参考にしてください。
■JNI編
基本的には、JNIの使い方です。
そこの中で、Android連携として引っかかりそうな部分だけピックアップして記述します。
●型
C言語上の型 unsigned char[]
JNI上の型 jbooleanArray
Java上の型 byte[]
と、C言語、JNI、Javaの層全部型名が違うパターンが存在します。
そのため、コード的には下記のような感じとなります。返り値などがC言語側とJava側で変化しているのがわかるかと思います。
・C言語側
JNIEXPORT
jbooleanArray
JNICALL
Java_org_siprop_opencv_OpenCV_findContours(JNIEnv* env, jobject thiz)
unsigned char ucharArray[128];
jbooleanArray res_array = env->NewBooleanArray(128);
env->SetBooleanArrayRegion(res_array, 0, 128, (jboolean*)ucharArray);
return res_array;
}
・Java側
public native byte[] findContours();
ちなみに、jni.hでは、下記のように定義されています。
#ifdef HAVE_INTTYPES_H
# include/* C99 */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
#else
typedef unsigned char jboolean; /* unsigned 8 bits */
typedef signed char jbyte; /* signed 8 bits */
typedef unsigned short jchar; /* unsigned 16 bits */
typedef short jshort; /* signed 16 bits */
typedef int jint; /* signed 32 bits */
typedef long long jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
#endif
- by noritsuna
- at 15:53