本文是基于维基百科中JNI英文文档翻译而来。
E文文档地址:http://en.wikipedia.org/wiki/Java_Native_Interface
一、缺陷
1.使用JNI细小的错误都能让这个JVM不稳定,并且这些错误很难再现和调试
2.使用JNI的应用失去了JAVA本身提供的不同平台的可移植性。
3.JNI 框架不提供自动的垃圾回收机制,所以这部分代码要考虑内存的释放
4.在linux 和solaris 平台,信号量过滤器会认为本地代码会处理信号量比JVM更好,所以如果本地代码注册自己为单处理(signal handler),他们就能够拦截到JVM的信号量。
5.在没有处理的异常中所有可能成为结果的都可能出现一些中断(比如空指针,除数为零),为了在中断到达JVM之前处理这些情况,在windows 平台,在try/catch 块中SHE(Structured Exception Handing)可能被使用去包装本地代码,以便获得机器(CPU/FPU)生成的软中断。
6.编码相关的方法是通过UTF-8修改而来:NewStringUTF, GetStringUTFLength, GetStringUTFChars, ReleaseStringUTFChars, GetStringUTFRegion,不是标准的UTF-8方法。程序应该使用NewString,GetStringChars,ReleaseStringChars, GetStringRegion, GetStringCritical, ReleaseStringCritical 方法,这些方法使用UTF-16LE 小尾端(little-endian)架构和UTF-16BE大尾端架构,然后用UTF-16到标准的UTF-8的转换。
二、类型映射
JNI基本类型和本地代码的类型映射
Native Type | Java Language Type | Description | Type signature |
---|---|---|---|
unsigned char | jboolean | unsigned 8 bits | Z |
signed char | jbyte | signed 8 bits | B |
unsigned short | jchar | unsigned 16 bits | C |
short | jshort | signed 16 bits | S |
long | jint | signed 32 bits | I |
long long | jlong | signed 64 bits | J |
float | jfloat | 32 bits | F |
double | jdouble | 64 bits | D |
void | V |
以上基本数据类型和本地代码可以直接使用,但是对于字符串和数组来说需要经过相应的转换才行
字符串
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString) { // printf("%s", javaString); // INCORRECT: Could crash VM! // Correct way: Create and release native string from Java string const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0); printf("%s", nativeString); (*env)->ReleaseStringUTFChars(env, javaString, nativeString); }
数组
JNIEXPORT jint JNICALL Java_IntArray_sumArray
(JNIEnv *env, jobject obj, jintArray arr) {
jint buf[10];
jint i, sum = 0;
// This line is necessary, since Java arrays are not guaranteed
// to have a continuous memory layout like C arrays.
env->GetIntArrayRegion(arr, 0, 10, buf);
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
关于JNIEnv
JNIEnv 是JNI方法的第一个参数,他提供了JNI环境和本地环境的相互作用,JNIEnv指针可以保存,但是之在当前线程才是合法的,其它线程必须先调用AttachCurrentThread()把当前依附到JVM上然后得到一个JNIEnv 的指针,之后这个本地线程就像一个JAVA线程一样工作了, 调用DetachCurrentThread()解除依附。
JNIEnv *env; (*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL); (*g_vm)->DetachCurrentThread (g_vm);