前言
在嵌入式底层工程师的世界里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.(JavaSayHello.java:4)
解决方法:
bupt@machine:~/miao/project/android/jni/myJNI$ java JavaSayHello
Get string from java :Hello world!
This is c env return ...