Java代碼 [JNITest.java]:java
package darcy; public class JNITest { static{ System.loadLibrary("Hello"); } public native void HelloKitty(); public static void main(String[] args){ new JNITest().HelloKitty(); } }
編譯Class文件,而後使用JDK提供的javah工具生成c++層的頭文件,固然也能夠手寫.linux
javah -jni darcy.JNITestios
運行後生成的頭文件內容[darcy_JNITest.h]:c++
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class darcy_JNITest */ #ifndef _Included_darcy_JNITest #define _Included_darcy_JNITest #ifdef __cplusplus extern "C" { #endif /* * Class: darcy_JNITest * Method: HelloKitty * Signature: ()V */ JNIEXPORT void JNICALL Java_darcy_JNITest_HelloKitty (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
這裏咱們能夠看到,Java層的HelloKitty()方法就對應上C++的Java_darcy_JNITest_HelloKitty,從這裏咱們能夠看出Java層對應的C++層方法簽名是Java_爲前綴+全限定方法名。jvm
實現這個方法 [darcy_JNITest.cpp]:工具
#include <jni.h> #include "darcy_JNITest.h" #include <stdio.h> JNIEXPORT void JNICALL Java_darcy_JNITest_HelloKitty (JNIEnv *, jobject){ printf("hello kitty\n"); }
下面使用g++編譯生成動態連接庫libHello.so。對應上Java層調用的System.loadLibrary("Hello");測試
從實現的c/c++代碼上看到,有個jni.h的頭文件須要引進來。該文件位於jdk自帶的include文件夾裏面,如個人在:/usr/java/jdk1.7.0_45/include這個下面.spa
這裏須要注意的是jni.h裏面須要依賴於一個叫作jni_md.h的文件,可是可能再也不在目錄下,向個人在/usr/java/jdk1.7.0_45/include/linux下面,若是報相關的編譯錯誤,調試
不妨到這裏面找一下。code
運行命令,生成.so文件:
g++ -shared -fPIC -I /usr/java/jdk1.7.0_45/include darcy_JNITest.cpp -o libHello.so
這裏要說明一下關於參數(-fPIC): 這是因爲我在編譯的時候出現了下面的錯誤:
/usr/local/bin/ld: /tmp/ccdq1TYq.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccdq1TYq.o: error adding symbols: 錯誤的值
因此才加上去的,不知道是否是跟以前爲了編譯linux內核而升級了ld這個東西有關,你們在調用該命令的時候能夠先把-fPIC去掉試一下。
好了,萬事具有,咱們運行一下Java程序看看:
java -Djava.library.path=. darcy.JNITest //在=的.表示當前路徑
output:hello kitty
文件的路徑圖:
Java代碼[JNIInvoke.Java]:
package darcy; public class JNIInvoke{ public static String invoke(){ System.out.println("This is from Java!"); return "Haha"; } public static void main(String[] args){ System.out.println(invoke()); } }
咱們將要在c++層調用的是invoke()方法,main只是用來測試.
第一步是編譯代碼,生成JNIInvoke.class。
C++代碼[JavaInvoke.cpp]:
#include <iostream> #include <jni.h> #include <stdlib.h> #include <string.h> using namespace std; string JStringToCString (JNIEnv *env, jstring str); char* jstringTostring(JNIEnv* env, jstring jstr); int main(){ JavaVM *jvm; /* denotes a Java VM */ JNIEnv *env; /* pointer to native method interface */ JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */ JavaVMOption* options = new JavaVMOption[1]; options[0].optionString = "-Djava.class.path=.:/usr/java/jdk1.7.0_45/lib"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = false; /* load and initialize a Java VM, return a JNI interface * pointer in env */ JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); delete options; /* invoke the Main.test method using the JNI */ jclass cls = env->FindClass("darcy/JNIInvoke"); jmethodID mid = env->GetStaticMethodID(cls, "invoke", "()Ljava/lang/String;"); jstring result = (jstring)env->CallStaticObjectMethod(cls, mid); cout<< jstringTostring(env,result) << endl; /* We are done. */ jvm->DestroyJavaVM(); return 0; } //把jstring類型轉化成c字符串,可不關注該方法 char* jstringTostring(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; }
這裏有個方法須要注意一下: GetStaticMethodID(), 其中最後一個參數是方法簽名(Signature),能夠經過javap -s darcy/JNIInvoke看到
接下來就是編譯C++代碼了。
由上面咱們能夠看到引進了頭文件jni.h,一樣的也是在/usr/java/jdk1.7.0_45/include/目錄下
編譯指令:
g++ -g -I/usr/java/jdk1.7.0_45/include/ -L/usr/java/jdk1.7.0_45/jre/lib/amd64/server JavaInvoke.cpp -ljvm
這裏用 -g是爲了方便gdb的調試,由於JNI的調用出錯以後很難定位錯誤,因此最後在懷疑出錯的地方單布調試一下。-L後面是編譯須要用到的庫的路徑。由於在這裏要用到libjvm.so這個庫。-ljvm就是定位的庫了。若是忘記加進來的話,編譯過程會報錯:
:對‘JNI_CreateJavaVM’未定義的引用
最後,爲了運行時能找到libjvm.so這個庫,要在環境變量中設置參數:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/java/jdk1.7.0_45/jre/lib/amd64/server
運行: ./a.out
output:
This is from Java! //Java層的打印
Haha //C/C++層的打印
文件的路徑圖: