在《從 HelloJNI 開始 Android NDK 的使用》這篇文章裡,可以看到 HelloJNI++ 專案的 jni/hellojni.c 裡實作了這段程式碼:
jstring Java_demo_example_hellojni_HelloActivity_stringFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !"); }
而在《使用 C++ 改寫 HelloJNI++》這篇文章裡,也可以在 jni/hellojni.cpp 看到了這段程式碼:
static const char *classPathName = "demo/example/hellojni/HelloActivity"; static JNINativeMethod methods[] = { {"stringFromJNI", "()Ljava/lang/String;", (void*)Java_demo_example_hellojni_HelloActivity_stringFromJNI }, };
這兩處的程式碼,都是對應到 HelloJNI++ 專案的 Java 程式碼:
package demo.example.hellojni; public class HelloActivity extends Activity { public native String stringFromJNI(); }
那麼,這裡的 Java_demo_example_hellojni_HelloActivity_stringFromJNI
和 ()Ljava/lang/String;
如何定義呢?在網路上找到的《JNI Examples for Android》文件提供了說明;此外,也可以使用 Java JDK 的 javah 這支程式來產生。
首先,我們來看一下 javah 的用途:
Usage: javah [options] <classes> where [options] include: -help Print this help message and exit -classpath <path> Path from which to load classes -bootclasspath <path> Path from which to load bootstrap classes -d <dir> Output directory -o <file> Output file (only one of -d or -o may be used) -jni Generate JNI-style header file (default) -version Print version information -verbose Enable verbose output -force Always write output files <classes> are specified with their fully qualified names (for instance, java.lang.Object).
javah 預設的用途,就是用來生成 JNI-style 的標頭檔。
那麼,我們來看一下怎麼使用它吧!最簡單的方法,開啟一個終端程式,然後到 Android 應用程式的專案路徑執行 javah 就可以了,例如以 HelloJNI++ 這個專案來示範。
方法非常簡單,在 HelloJNI++ 專案的路徑執行這個指令:
$ javah -classpath bin/classes -d jni/ demo.example.hellojni.HelloActivity
其中,要注意的是 classpath 的路徑要指向 bin/classes,而不是 src/ 。執行完成後,就會在 jni/ 路徑下看到生成的標頭檔 demo_example_hellojni_HelloActivity.h 了。標頭檔 demo_example_hellojni_HelloActivity.h 的內容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class demo_example_hellojni_HelloActivity */ #ifndef _Included_demo_example_hellojni_HelloActivity #define _Included_demo_example_hellojni_HelloActivity #ifdef __cplusplus extern "C" { #endif /* * Class: demo_example_hellojni_HelloActivity * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_demo_example_hellojni_HelloActivity_stringFromJNI (JNIEnv *, jobject); /* * Class: demo_example_hellojni_HelloActivity * Method: unimplementedStringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_demo_example_hellojni_HelloActivity_unimplementedStringFromJNI (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
往後,只要在 Java 程式碼裡宣告了 native 的函式,那麼就需要使用 javah 來重新生成這個標題檔,很容易吧!
接著再來看看,既然使用了 Eclipse 這個開發環境,那麼能不能直接將 javah 的動作整合到 Eclipse 裡呢?方法就是使用 Eclipse 的 External Tools。
從 Eclipse 的 Run 下拉選單找到 External Tools,然後點擊 External Tools Configurations。
然後選擇 Program,並新建一個設定。
接著,開始填入以下內容:
- Name 的部份任意填入一個容易分辨的名稱。
- Location 填入 javah 的路徑,由於我在 Linux 下操作,因此一般為
/usr/bin/javah
。 - Working Directory 填入
${workspace_loc:/HelloJNI++/bin}
,其中 HelloJNI++ 對應到 Android 應用程式專案的名稱。也可以按下 Browse Worksapce 鈕來選擇。 - Arguments 填入
-classpath ${workspace_loc:/HelloJNI++/bin/classes} -d ${workspace_loc:/HelloJNI++/jni} demo.example.hellojni.HelloActivity
,這個是提供給 javah 的參數,其中 HelloJNI++ 同樣對應到 Android 應用程式專案的名稱。
接著,到 Refresh 頁面,勾選 Specific resources,然後按下右側的 Specific Resources 鈕,並將 HelloJNI++ 專案裡的 jni 及 src 兩個目錄勾選起來。
設定完成後,只要從 External Tools 執行這個項目,就會生成 HelloJNI++ 專案的 JNI 標頭檔了。