Java + JNI 本地庫 異構程序構建示例。
下文 (增強版) : http://my.oschina.net/typhoon/blog/470904java
[typhoon@TFW-CENT6-LT sandbox]$ mkdir -p src/tfw/rsch/jni [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt src [typhoon@TFW-CENT6-LT sandbox]$ vi src/tfw/rsch/jni/Main.java [typhoon@TFW-CENT6-LT sandbox]$ vi src/tfw/rsch/jni/JniLoader.java [typhoon@TFW-CENT6-LT sandbox]$ vi src/tfw/rsch/jni/JniCall.java [typhoon@TFW-CENT6-LT sandbox]$ vi src/tfw/rsch/jni/JniCall_02.java [typhoon@TFW-CENT6-LT sandbox]$ ls src/tfw/rsch/jni JniCall_02.java JniCall.java JniLoader.java Main.java [typhoon@TFW-CENT6-LT sandbox]$ |
/** * ...<br /> */ package tfw.rsch.jni; import tfw.base.util.array.ArrayToolE; import tfw.base.util.text.TextToolE; /** * 啓動類,啓動整個程序。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-23_00-06 */ public class Main { /** * 主函數,啓動加載和測試 JNI 的用例。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-09_15-54 * @param str1dCmdArgs * - 字符串型數組,來自命令行的參數。<br /> */ public static void main(String[] str1dCmdArgs) { new Main().test(str1dCmdArgs); } /** * JNI 加載與調用測試。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-23_00-05 * @param str1dArgs * 來自命令行的參數,字符串型數組。<br /> */ private void test(String[] str1dArgs) { try { // [S] 對數組型參數作預處理,把數組元素組合進單一的字符串。 String strArgsText = ArrayToolE.arrayForConsole(str1dArgs); if (null == strArgsText) { strArgsText = ""; } else if ("null".equals(strArgsText)) { strArgsText = null; } // [E] 對數組型參數作預處理,把數組元素組合進單一的字符串。 // 測試用例 01 :加載本地庫。 new JniLoader(); // 測試用例 01 :調用本地函數 (本地庫中的函數)。 JniCall jc = new JniCall(); jc.println(); jc.nativePrintln(); // [S] 測試用例 02 :加載本地庫、調用本地函數。 JniCall_02 jc_02 = new JniCall_02(); { { String strMethodHead = TextToolE.concat("\tjc_02.javaManipulate(", ((null == strArgsText) ? "null" : ("\"" + strArgsText + "\"")) + ")\n\t{"); System.out.println(strMethodHead); String strRst = jc_02.javaManipulate(strArgsText); String strMethodTail = TextToolE.concat("\t}\n\tGot Return Value: ", ((null == strRst) ? "null" : ("\"" + strRst + "\""))); System.out.println(strMethodTail); } System.out.println(); { String strMethodHead = TextToolE.concat("\tjc_02.nativeManipulate(", ((null == strArgsText) ? "null" : ("\"" + strArgsText + "\"")) + ")\n\t{"); System.out.println(strMethodHead); // 此處:調用本地函數! String strOut = jc_02.nativeManipulate(strArgsText); String strMethodTail = TextToolE.concat("\t}\n\tGot Return Value: ", ((null == strOut) ? "null" : ("\"" + strOut + "\""))); System.out.println(strMethodTail); } } // [E] 測試用例 02 :加載本地庫、調用本地函數。 } catch (Throwable t) { t.printStackTrace(); } } }
/** * ...<br /> */ package tfw.rsch.jni; /** * 測試用例 01 :<br /> * 這個類在初始化時加載測試用例 01 的本地庫 (*.so 共享模塊 或 *.dll 動態連接庫 * 之類)。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-22_23-12 */ public class JniLoader { /** * 靜態代碼塊,確保在類初始化時即自動執行共享庫加載函數。<br /> */ static { loadNativeLibrary(); } /** * 測試用例 01 :<br /> * 本地庫 (*.so 共享模塊 或 *.dll 動態連接庫 之類) 加載函數。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-22_21-37 */ private static void loadNativeLibrary() { // 將從系統屬性「java.library.path」中搜索本地庫。 String strJavaLibraryPath = System.getProperty("java.library.path"); // 將根據本地庫的名稱「JniCall_name1」加載相應的庫文件 // 「libJniCall_name1.so」(或「JniCall_name1.dll」之類)。 String strNativeLibraryName = "JniCall_name1"; System.out.println("java.library.path=" + strJavaLibraryPath); System.out.println("Loading \"" + strNativeLibraryName + "\"..."); // 此處:加載本地庫! System.loadLibrary(strNativeLibraryName); System.out.println("Loaded."); } }
/** * ...<br /> */ package tfw.rsch.jni; /** * 測試用例 01 :<br /> * 這個類提供一個測試用例 01 本地庫中函數 (本地函數) 的入口,供 java 程序調用。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-22_23-32 */ public class JniCall { /** * 測試用例 01 :一個 java 函數。向標準輸出打印一條信息。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-11_14-09 */ public void println() { System.out.println("\tJava:\tWorks!"); } /** * 測試用例 01 :本地庫中函數 (本地函數) 的入口。<br /> * 預計相應的本地函數也將向標準輸出打印一條信息,相似於上述的 java 函數。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-11_14-09 */ public native void nativePrintln(); }
/** * ...<br /> */ package tfw.rsch.jni; import tfw.base.util.misc.MiscToolE; import tfw.base.util.text.TextToolE; /** * 測試用例 02 :這個類<br /> * * 在初始化時加載測試用例 01 的本地庫 (*.so 共享模塊 或 *.dll 動態連接庫 之類);<br /> * * 提供一個測試用例 02 本地庫中函數 (本地函數) 的入口,供 java 程序調用。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-22_23-56 */ public class JniCall_02 { /** * 靜態代碼塊,確保在類初始化時即自動執行共享庫加載函數。<br /> */ static { loadNativeLibrary(); } /** * 測試用例 02 :<br /> * 本地庫 (*.so 共享模塊 或 *.dll 動態連接庫 之類) 加載函數。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-22_23-15 */ private static void loadNativeLibrary() { // 將從系統屬性「java.library.path」中搜索本地庫。 String strJavaLibraryPath = System.getProperty("java.library.path"); // 將根據本地庫的名稱「JniCall_02」加載相應的庫文件「libJniCall_02.so」 // (或「JniCall_02.dll」之類)。 String strNativeLibraryName = "JniCall_02"; System.out.println("java.library.path=" + strJavaLibraryPath); System.out.println("Loading \"" + strNativeLibraryName + "\"..."); // 此處:加載本地庫! System.loadLibrary(strNativeLibraryName); System.out.println("Loaded."); } /** * 測試用例 02 :一個 java 函數。<br /> * 將函數入口處傳入的字符串參數打印至標準輸出; * 以後從標準輸入處讀取一個字符串,並將其返回。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-11_14-10 * @param strText * - 傳入的參數,字符串。<br /> * @return 一個收自標準輸入的字符串,<strong>有可能爲 null 。</strong><br /> */ public String javaManipulate(String strText) { // 將傳入的字符串參數打印至標準輸出。 String strReceiveText = TextToolE.concat("\t\tJava:\n\n\t\tArgument Received:\n\t\t\t", (null == strText) ? "null" : ("\"" + strText + "\"")); System.out.println(strReceiveText); // [S] 從標準輸入處接收一個字符串,並打印至標準輸出。 System.out .print("\t\tUser Input (\"null\" would be considered as null):\n\t\t\t"); String strUserInput = MiscToolE.getline(System.in); System.out.println("\t\tUser Input Received:\n\t\t\t" + ((null == strUserInput) ? "null" : ("\"" + strUserInput + "\""))); // [E] 從標準輸入處接收一個字符串,並打印至標準輸出。 // [S] 把將要返回的值打印至標準輸出。 String strReturnValue = "null".equals(strUserInput) ? null : strUserInput; System.out.println("\t\tReturns:\n\t\t\t" + ((null == strReturnValue) ? "null" : ("\"" + strReturnValue + "\""))); // [E] 把將要返回的值打印至標準輸出。 // 返回。 return strReturnValue; } /** * 測試用例 02 :本地庫中函數 (本地函數) 的入口。<br /> * 預計相應的本地函數也將把函數入口處傳入的字符串參數打印至標準輸出、 * 以後從標準輸入處讀取一個字符串,並將其返回。<br /> * * @author Typhoon.Free.Wolf * @version 2015-04-11_14-10 * @param strText * - 傳入的參數,字符串。<br /> * @return 一個實際上返回自本地函數的字符串,<strong>有可能爲 null 。</strong><br /> */ public native String nativeManipulate(String strText); }
[typhoon@TFW-CENT6-LT sandbox]$ cp -a ../lib . # ← 上述源代碼引用的工具類在這個文件夾裏。 [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt lib src [typhoon@TFW-CENT6-LT sandbox]$ ls lib tfw-base.aij.jar tfw-base.v2.2.8_2014-12-22_22-00.longest_night.jre150.aij.jar # ↑ ↖_ 工具類「ArrayToolE」、「MiscToolE」和「TextToolE」所在。 # `- 指向「tfw-base.v2.2.8_2014-12-22_22-00.longest_night.jre150.aij.jar」的符號連接。 [typhoon@TFW-CENT6-LT sandbox]$ mkdir -p classes [typhoon@TFW-CENT6-LT sandbox]$ javac -classpath lib/tfw-base.aij.jar -d classes src/tfw/rsch/jni/*.java [typhoon@TFW-CENT6-LT sandbox]$ ls classes/tfw/rsch/jni JniCall_02.class JniCall.class JniLoader.class Main.class [typhoon@TFW-CENT6-LT sandbox]$ |
[typhoon@TFW-CENT6-LT sandbox]$ mkdir c_include [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt c_include classes lib src [typhoon@TFW-CENT6-LT sandbox]$ javah -classpath classes -o c_include/JniCall_name2.h tfw.rsch.jni.JniCall [typhoon@TFW-CENT6-LT sandbox]$ javah -classpath classes -o c_include/JniCall_02.h tfw.rsch.jni.JniCall_02 [typhoon@TFW-CENT6-LT sandbox]$ ls c_include JniCall_02.h JniCall_name2.h [typhoon@TFW-CENT6-LT sandbox]$ |
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class tfw_rsch_jni_JniCall */ #ifndef _Included_tfw_rsch_jni_JniCall #define _Included_tfw_rsch_jni_JniCall #ifdef __cplusplus extern "C" { #endif /* * Class: tfw_rsch_jni_JniCall * Method: nativePrintln * Signature: ()V */ JNIEXPORT void JNICALL Java_tfw_rsch_jni_JniCall_nativePrintln (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class tfw_rsch_jni_JniCall_02 */ #ifndef _Included_tfw_rsch_jni_JniCall_02 #define _Included_tfw_rsch_jni_JniCall_02 #ifdef __cplusplus extern "C" { #endif /* * Class: tfw_rsch_jni_JniCall_02 * Method: nativeManipulate * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_tfw_rsch_jni_JniCall_102_nativeManipulate (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
[typhoon@TFW-CENT6-LT sandbox]$ mkdir c_src [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt c_include classes c_src lib src [typhoon@TFW-CENT6-LT sandbox]$ vi c_src/JniCall_name3.c [typhoon@TFW-CENT6-LT sandbox]$ vi c_src/JniCall_02.c [typhoon@TFW-CENT6-LT sandbox]$ ls c_src JniCall_02.c JniCall_name3.c [typhoon@TFW-CENT6-LT sandbox]$ |
#include <jni.h> #include <stdio.h> #include <JniCall_name2.h> JNIEXPORT void JNICALL Java_tfw_rsch_jni_JniCall_nativePrintln (JNIEnv *env, jobject obj) { printf("\tJNI:\tWorks!\n"); return; };
#include <jni.h> #include <stdio.h> #include <JniCall_02.h> JNIEXPORT jstring JNICALL Java_tfw_rsch_jni_JniCall_102_nativeManipulate (JNIEnv *jniEnv, jobject jobj, jstring jstr) { // 將傳入的參數打印至標準輸出。 printf("\t\tJNI:\n\n\t\tArgument Received:\n\t\t\t"); printf((NULL == jstr) ? "%s\n" : "\"%s\"\n", jstr); // 將傳入的字符串參數轉換成 C 語言字符串,再打印至標準輸出。 const char *strConverted = (NULL == jstr) ? NULL : (*jniEnv)->GetStringUTFChars(jniEnv, jstr, 0); printf("\t\tConverted:\n\t\t\t"); printf((NULL == strConverted) ? "%s\n" : "\"%s\"\n", strConverted); // [S] 從標準輸入處接收一個字符串,並打印至標準輸出。 printf("\t\tUser Input (\"NULL\" would be considered as NULL):\n\t\t\t"); char *ch1dUserInput; gets(ch1dUserInput); printf("\t\tUser Input Received:\n\t\t\t"); printf((0 == strcmp(ch1dUserInput, "NULL")) ? "%s\n" : "\"%s\"\n", ch1dUserInput); // [E] 從標準輸入處接收一個字符串,並打印至標準輸出。 // 把接收到、將要返回的字符串打印至標準輸出。 printf("\t\tReturn Value:\n\t\t\t"); char *strReturnValue = (0 == strcmp(ch1dUserInput, "NULL")) ? NULL : ch1dUserInput; printf((NULL == strReturnValue) ? "%s\n" : "\"%s\"\n", strReturnValue); // 把將要返回的字符串轉換成 java 字符串,並打印至標準輸出。 jstring jstrReturnValue = (*jniEnv)->NewStringUTF(jniEnv, strReturnValue); printf("\t\tConverted:\n\t\t\t"); printf((NULL == jstrReturnValue) ? "%s\n" : "\"%s\"\n", jstrReturnValue); // 返回。 return jstrReturnValue; };
[typhoon@TFW-CENT6-LT sandbox]$ vi jnicc.sh [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt c_include classes c_src jnicc.sh lib src [typhoon@TFW-CENT6-LT sandbox]$ cat jnicc.sh #!/bin/sh # date; echo $0; echo $1; echo $2; # gcc $1 -shared -I c_include -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -o $2; # gcc 4.4.7 20120313 of CentOS 6.6 requires "-fPIC". gcc $1 -fPIC -shared -I c_include -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -o $2; echo $?; # date; [typhoon@TFW-CENT6-LT sandbox]$ chmod 700 jnicc.sh [typhoon@TFW-CENT6-LT sandbox]$ |
[typhoon@localhost sandbox]$ ./jnicc.sh c_src/JniCall_name3.c lib/libJniCall_name1.so ./jnicc.sh c_src/JniCall_name3.c lib/libJniCall_name1.so 0 [typhoon@localhost sandbox]$ ./jnicc.sh c_src/JniCall_02.c lib/libJniCall_02.so ./jnicc.sh c_src/JniCall_02.c lib/libJniCall_02.so 0 [typhoon@localhost sandbox]$ ls lib libJniCall_02.so libJniCall_name1.so tfw-base.aij.jar tfw-base.v2.2.8_2014-12-22_22-00.longest_night.jre150.aij.jar [typhoon@TFW-CENT6-LT sandbox]$ |
[typhoon@TFW-CENT6-LT sandbox]$ vi jrun.alias [typhoon@TFW-CENT6-LT sandbox]$ ls 0_guide.txt c_include classes c_src jnicc.sh jrun.alias lib src [typhoon@TFW-CENT6-LT sandbox]$ cat jrun.alias alias jrun='java -classpath classes:lib/tfw-base.aij.jar -Djava.library.path=lib'; [typhoon@TFW-CENT6-LT sandbox]$ |
[typhoon@TFW-CENT6-LT sandbox]$ source jrun.alias [typhoon@TFW-CENT6-LT sandbox]$ alias …… alias jrun='java -classpath classes:lib/tfw-base.aij.jar -Djava.library.path=lib' …… [typhoon@localhost sandbox]$ jrun tfw.rsch.jni.Main 一二三四五 上山打老虎 java.library.path=lib Loading "JniCall_name1"... Loaded. Java: Works! JNI: Works! java.library.path=lib Loading "JniCall_02"... Loaded. jc_02.javaManipulate("一二三四五, 上山打老虎") { Java: Argument Received: "一二三四五, 上山打老虎" User Input ("null" would be considered as null): 老虎沒打到,打到小松鼠…… User Input Received: "老虎沒打到,打到小松鼠……" Returns: "老虎沒打到,打到小松鼠……" } Got Return Value: "老虎沒打到,打到小松鼠……" jc_02.nativeManipulate("一二三四五, 上山打老虎") { JNI: Argument Received: "h#��" Converted: "一二三四五, 上山打老虎" User Input ("NULL" would be considered as NULL): 松鼠有幾隻?一二三四五! User Input Received: "松鼠有幾隻?一二三四五!" Return Value: "松鼠有幾隻?一二三四五!" Converted: "�I��" } Got Return Value: "松鼠有幾隻?一二三四五!" [typhoon@localhost sandbox]$ |