前言
在嵌入式底層工程師的世界裏JNI就是java跟c/c++世界溝通的橋樑,包括我也是這樣認爲了很多年;前幾天跟做app的同事聊天,無意中發現在他們的知識體系中jni是Java世界和Native世界的媒介。"Native"沒錯這個纔是理解的關鍵。在java語言出現之前,就有很多程序和庫都是由Native語言寫的,利用現有的庫開發事半功倍同時可以保證更好的性能。 就是好比Android的底層是linux內核——c語言世界的極致表達。 今天整理一下JNI。
一、通過一個案例開始說起吧java讓c說聲"Hello world!"
1、java
public class JavaSayHello{
/*通過構造塊來加載C庫*/
static{
System.loadLibrary("native");
}
public native String java_say_to_c(String str); //聲明爲native方法
public static void main (String args[]){
JavaSayHello oTmp = new JavaSayHello(); //執行此句話時會調用構造塊
/*調用c中的映射的函數打印Hello world!*/
System.out.println(oTmp.java_say_to_c("Hello world!"))
}
}2、c (c程序裏的工作量比較多,包括建立映射表、JNI加載C庫、被調用的c程序)
1)映射表結構
static const JNINativeMethod methods[] = {
{"java_say_to_c", "(Ljava/lang/String;)Ljava/lang/String;", (void *)c_get_from_java},
};
2)c程序
jstring JNICALL c_get_from_java(JNIEnv *env, jobject cls, jstring str)
{
const jbyte *cstr;
cstr = (*env)->GetStringUTFChars(env, str, NULL);
if (cstr == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("Get string from java :%s\n", cstr);
(*env)->ReleaseStringUTFChars(env, str, cstr);
return (*env)->NewStringUTF(env, "This is c env return...\n");
}
3)系統加載c函數
/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
}
cls = (*env)->FindClass(env, "JavaSayHello");
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map (java) java_say_to_c<-->(c) c_get_from_java*/
if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) //這個數字1代表映射了一個方法映射數組的個數
return JNI_ERR;
return JNI_VERSION_1_4;
}
基本邏輯可以理解爲:
1、java在實例化對象oTmp時構造塊會加載C庫(System.loadLibrary("native");)
2、JNI_OnLoad會先獲取java環境,通過環境找到java中的類(這部分自己理解),然後通過methods建立的數組映射表,建立一一映射關係。
3、在java中調用 方法,方法通過映射找到函數,然後調用對應的c函數。
二、實際編譯運行測試:
1、
編譯c文件 native.c: gcc -I /home/bupt/miao/box/soft/java-7-openjdk-amd64/include/ -fPIC -shared -o libnative.so native.c
-I /home/bupt/miao/box/soft/java-7-openjdk-amd64/include/ 是指定編譯的中jni.h的路徑,根據自己的JDK安裝路徑選擇。
2、
編譯java:javac JavaSayHello.java
注:編譯錯誤
Exception in thread "main" java.lang.UnsatisfiedLinkError: no native in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1889)
at java.lang.Runtime.loadLibrary0(Runtime.java:849)
at java.lang.System.loadLibrary(System.java:1088)
at JavaSayHello.<clinit>(JavaSayHello.java:4)
解決方法:運行結果:
bupt@machine:~/miao/project/android/jni/myJNI$ java JavaSayHello
Get string from java :Hello world!
This is c env return ...