件の記事[Androidで、目指せ脱ざんねん] ですが、
Native移植をするためにはあの記事の情報だけでは実は不足なのです。
当方の都合により一部説明削っておりました...
すいません...
まとめておきますので、もし必要ならご参照ください...
作法がC++とCで異なる模様
jniで用いるnativeコードの書き方ですが、C++とCで書き方を変えないといけない模様です。
拡張子cでコンパイルの通ったコードも、拡張子cppに変更するととたんにエラーになります...
AndroidのnativeソースコードはほとんどがC++で書かれているため、
以下はC++記述に限った作法の説明になります。
本当は間にVMが入る
記事中で掲示したソフト構成図ですが、もう少し正しく書くとこうなります。
Javaライブラリの下にVM層があります。
ライブラリロード時
カスタムしたライブラリの関数を呼び出す前に、
Java側でライブラリのロード処理が必要です。
Javaで以下の記述を追加します。
・MtpDevice.java ([app]/src/android/mtp/custom/MtpDevice.java)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static { | |
System.loadLibrary("mtpcustom"); | |
} |
ライブラリロードの指示を受けて、VMがNativeモジュール(ここではsoファイル生成に使用するソースファイル群)に実装されたJNI_OnLoad関数を呼びます。NativeモジュールはここでJavaから呼び出されるべきメソッドをVMに登録するため。AndroidRuntime::registerNatieMethodsを呼びだします。この関数でメソッドを登録することで初めてJavaからJNI以下の呼び出せるようになります。
実装は以下のようになります。 オリジナルではJNI_OnLoad関数はandroid_mtp_MediaPlayer.cppに実装されていましたが、
移植にあたってこのファイルは不要として移植対象から外したため、
移植するandroid_mtp_MtpDevice.cppに実装しなおしています。
(コピペですが... )
・android_mtp_MtpDevice.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 元はandroid_media_MediaPlayer.cppに実装されていた関数 | |
jint JNI_OnLoad(JavaVM* vm, void* reserved) | |
{ | |
// 中略 | |
if (register_android_mtp_MtpDevice(env) < 0) { | |
ALOGE("ERROR: MtpDevice native registration failed"); | |
goto bail; | |
} | |
// 中略 | |
} | |
int register_android_mtp_MtpDevice(JNIEnv *env) | |
{ | |
// 中略 | |
return AndroidRuntime::registerNativeMethods(env, | |
"android/mtp/MtpDevice", gMethods, NELEM(gMethods)); | |
} |
ここで、第三引数に渡すgMethodsはJNINativeMethod型構造体の配列になります。
今回の場合は以下の内容で渡しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static JNINativeMethod gMethods[] = { | |
{"native_open", "(Ljava/lang/String;I)Z", | |
(void *)android_mtp_MtpDevice_open}, | |
{"native_close", "()V", (void *)android_mtp_MtpDevice_close}, | |
{"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;", | |
(void *)android_mtp_MtpDevice_get_device_info}, | |
{"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids}, | |
{"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;", | |
(void *)android_mtp_MtpDevice_get_storage_info}, | |
{"native_get_object_handles","(III)[I", | |
(void *)android_mtp_MtpDevice_get_object_handles}, | |
{"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;", | |
(void *)android_mtp_MtpDevice_get_object_info}, | |
{"native_get_object", "(II)[B",(void *)android_mtp_MtpDevice_get_object}, | |
{"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail}, | |
{"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object}, | |
{"native_get_parent", "(I)J", (void *)android_mtp_MtpDevice_get_parent}, | |
{"native_get_storage_id", "(I)J", (void *)android_mtp_MtpDevice_get_storage_id}, | |
{"native_import_file", "(ILjava/lang/String;)Z", | |
(void *)android_mtp_MtpDevice_import_file}, | |
// kassy_kz add start | |
{"native_do_device_shutter", "()Landroid/mtp/MtpDeviceInfo;", | |
(void *)android_mtp_MtpDevice_do_device_shutter}, | |
{"native_set_shutter_speed", "(I)Z", | |
(void *)android_mtp_MtpDevice_set_shutter_speed}, | |
{"native_set_aperture", "(I)Z", | |
(void *)android_mtp_MtpDevice_set_aperture}, | |
// kassy_kz add end | |
}; |
JNINativeMethodについて
JNINativeMethod型構造体のルールはちょっと特殊なのでここで簡単に解説をば...
JNINativeMethod | ||
const char* | name | Javaから呼んで欲しい関数の名前 |
const char* | signature | 関数の引数と戻り値の型 |
void* | fnPtr | 実際に呼び出すNaive関数の関数ポインタ |
特殊なのは第二引数。文字列リテラルをぶっこむことで引数・戻り値の型を指定しますが、
指定の仕方が特殊です。
文字列リテラルの括弧の中に引数の型、括弧の後ろに戻り値の型を指定します。
型の指定の仕方は以下のとおり
文字 | 型(Javaから) | 型(Nativeから) |
I | Int型 | jint |
J | long型 | jlong |
Z | boolean型 | jboolean |
V | void型 | void |
F | float型 | jfloat |
[B | (直後の文字Bの)バイト配列 | jbytearray |
Ljava/lang/String(L + クラス名) | java.lang.String型 | jobject |
機能呼び出し時
上記登録作業を済ませれば、Java側から無事呼び出すことが可能になります。
ここから先は先の記事で紹介したとおりです。
・Java部
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public MtpDeviceInfo doDeviceShutter() { | |
return native_do_device_shutter(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// シャッターをきるコマンド、 android_mtp_MtpDevice_get_device_infoをほぼまるパク | |
static jobject | |
android_mtp_MtpDevice_do_device_shutter(JNIEnv *env, jobject thiz) | |
{ | |
MtpDevice* device = get_device_from_object(env, thiz); | |
if (!device) { | |
ALOGD("android_mtp_MtpDevice_get_device_info device is null"); | |
return NULL; | |
} | |
MtpDeviceInfo* deviceInfo = device->doDeviceShutter(); | |
if (!deviceInfo) { | |
ALOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null"); | |
return NULL; | |
} | |
env->NewObject(clazz_deviceInfo, constructor_deviceInfo); | |
return null; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// シャッターを切るコマンド getDeviceInfo()をほぼまるパク | |
MtpDeviceInfo* MtpDevice::doDeviceShutter() { | |
Mutex::Autolock autoLock(mMutex); | |
mRequest.reset(); | |
if (!sendRequest(0x910f)) | |
return NULL; | |
if (!readData()) | |
return NULL; | |
MtpResponseCode ret = readResponse(); | |
if (ret == MTP_RESPONSE_OK) { | |
MtpDeviceInfo* info = new MtpDeviceInfo; | |
info->read(mData); | |
return info; | |
} | |
return NULL; | |
} |
0 件のコメント:
コメントを投稿