私は自分のjniソースを書き込もうとしています。いくつかのndkサンプルを見ると、Javaパッケージの名前の後に続くこれらのマクロJNIEXPORTおよびJNICALLをよく使用していることがわかりました
JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms)
ググったけど、いつどのようにこれらのマクロを使うのか理解できない
JNIEXPORTおよびJNICALLは、NDK_ROOT/platforms/Android-9/Arch-arm/usr/include/jni.hで定義されています。設定に応じて、このパスは異なりますが、ほとんど同じです。
#define JNIIMPORT
#define JNIEXPORT __attribute__ ((visibility ("default")))
#define JNICALL
JNIEXPORTは、ビルドされたバイナリ(* .soファイル)の動的テーブルにネイティブ関数を表示するために使用されます。 「非表示」または「デフォルト」に設定できます(詳細 こちら )。これらの関数が動的テーブルにない場合、JNIはそれらを呼び出す関数を見つけることができないため、RegisterNatives呼び出しは実行時に失敗します。
すべての関数がデフォルトで動的テーブルに格納されるため、誰でも簡単にネイティブコードを逆コンパイルできることに注意してください。 JNIが見つける必要がある場合に備えて、すべての関数呼び出しはバイナリに組み込まれています。これは、コンパイラオプション-fvisibility
を使用して変更できます。これを-fvisibility=hidden
に設定してコードを安全に保ち、JNIEXPORTを使用して関数に外部の可視性があることを示すフラグを付けることをお勧めします。
Stripコマンドを使用すると、デバッグシンボルが削除されるだけで、動的テーブルは分離されます。 objdumpで遊んで、.soファイルから人がどれだけ得ることができるかを確認してください。
私たちは最近これにつまずきました、これが誰かを助けることを願っています。
編集:カスタムビルドシステムを使用しているため、他のビルド設定では、可視性オプションがデフォルトで設定される場合があります。詳細については this SO answer を参照してください。
これらのマクロの定義は、JNIインクルードのマシン依存部分にあります(通常は$Java_HOME/include/<Arch>/jni-md.h
)。
つまり、JNIEXPORT
には、指定された関数が適切にエクスポートされるようにするために必要なコンパイラディレクティブが含まれています。 Android(および他のLinuxベースのシステム)では、空になります。
JNICALL
には、指定された関数が適切な呼び出し規約で処理されるようにするために必要なコンパイラディレクティブが含まれています。 Androidでもおそらく空です(w32では__stdcallです)。
一般に、空の場合でもそのままにしておく必要があります#define
s。
簡単な言葉で:
JNIEXPORT
registerNatives
ファミリーの関数を使用する必要がある場合は、JNIEXPORTを使用しないでください。それ以外の場合は使用する必要があります。JNICALL
は常に使用する必要があります。JNIEXPORTは、関数がシンボルテーブルに表示されるようにします。 JNICALLは、関数が正しい呼び出し規約を使用することを保証します。 Android JNICALLはアーキテクチャに基づいて異なる値を持っています。ARMは空なので、含まれないように騙されます。しかし、JNICALLを使用する必要があります。
registerNatives
を使用すると、関数をJNI_onLoad
でプログラム的に、または将来的にリンクすることができます。 registerNatives
を使用すると、以前に間違った関数名をキャッチできます。この方法をお勧めします。
ネイティブクラスで「javah」を実行し、生成されたものを使用してください。 100%の信頼性でそれを生成できるツールがあれば、これの詳細を知る必要はありません。