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]$ |