JNI是Java本地接口。它定义了管理代码的方法并与本地代码互动,直接的说就是java与C/C++的互动。我们应该认真的了解一下j2se 6上的JNI。了解它的功能。更详细的信息可以访问Jva官网。
JNI定义了两个关键的数据结构,JavaVM和JNIEnv。本质上来说你可以吧它们理解为函数表的指针。JavaVM 提供调用接口来创建和销毁它自己,从理论上来说每个进程允许有多个JavaVMs ,但在android上只允许一个。JNIEnv提供大部分JNI函数。你的本地函数都接收一个JNIEnv 做为第一个参数。
JNIEnv是用于本地线程存储的。理由是你不能在两个线程之间共享一个JNIEnv。如果一段代码没有方法获得它的JNIEnv,你就应该共享JavaVM,并使用GetEnv来发现线程的JNIEnv。
在C上声明JNIEnv 和JavaVM 与C++上的声明是不同的。"jni.h" include文件提供不同的typedefs ,这就取决于你include的是C还是C++了。出于这个原因,在两种语言的头文件中包含JNIEnv 参数是一个不好的做法。
所有线程都是Linux线程,并由内核调度。它们通常从托管代码开始(例如 Thread.start),但它们也能从别处创建然后连接到JavaVM。例如,一个线程使用pthread_create创建,然后被JNI 的AttachCurrentThread 或AttachCurrentThreadAsDaemon 函数来连接。在一个线程被连接之前,它没有JNIEnv就不能使用JNI调用 。
Android 不会挂起正在执行本地代码的线程。如果Gc(垃圾回收)正在处理,或者debugger 发出一个挂起请求,android将会在下一次暂停这个线程并使用一个JNI调用。
另外我们需要通过JNI调用DetachCurrentThread来连接线程,前提是这些线程在退出之前我们就要执行调用。
如果你想要从本地代码访问一个对象的值域(或者叫属性),你应该这么做:
同样的,如果你想调用一个方法,首先你获得一个类对象的引用然后获得一个方法ID。这些ID往往都是内部运行时数据结构中的指针。寻找他们可能需要几个String的比较,一旦你实际调用过它们,那么获得值域或调用方法是非常快的。
如果性能要求比较高的话,寻找这些值并在你的网本地代码中缓存结果是很有用的。因为每一个进程只有一个JavaVM的限制,在一个静态本地结构存储这些数据是很合理的。
在类被卸载之前,它的引用,值域ID和方法ID是保证有效的。
当一个类被载入时,如果你想要缓存ID,并且想要当类被卸载和重载时自动重新缓存它们,正确的方法就是初始化这些ID并添加下面一段代码:
private static native void nativeInit(); //静态块语法,不熟悉的请先补java static { nativeInit(); }
请注意上面代码只执行一次,就是当类被第一次初始化时,不管你这个类被new多少次nativeInit()只执行一次,除非你把这个类卸载掉,在重新载入它才会被执行。
java程序语言使用的是UTF-16。为方便起见,JNI提供方法使它修改后工作在UTF-8上。修改后的编码对C来说是有用的,因为它用0xc0 0x80编码\u0000而不是用0x00。
如果可能的话,使用UTF-16会更快。
1、JNI异步条件下(多线程/回调函数),如何取得JNIEnv
使用AttachCurrentThread()函数。
示例代码:
#ifdef JNI_VERSION_1_4 jint res = cached_jvm->AttachCurrentThread((void**)&env, NULL); #else jint res = cached_jvm->AttachCurrentThread(&env, NULL); #endif; if (env == NULL) return; jclass clsSctp = env->FindClass("com/sunrising/nettest/netpackage/implement/SCTPTransImplement"); if (clsSctp == NULL) { ThrowNullPointerException(env, "SCTP class is null"); cached_jvm->DetachCurrentThread(); return; } jmethodID mid = env->GetStaticMethodID(clsSctp, "communicationErrorNotify", "(JJ)V"); if (mid == 0) { ThrowNullPointerException(env, "Method communicationErrorNotify() is null"); cached_jvm->DetachCurrentThread(); return; } env->CallStaticVoidMethod(clsSctp, mid, jlAssociationID, jlErrorType); cached_jvm->DetachCurrentThread();
2、使用GetStringUTFChars()之后要注意释放内存
使用上述函数之后,要使用ReleaseStringUTFChars()函数释放字符串内存。
示例代码:
pBuffer = env->GetStringUTFChars(jsAmrFile, &isCopy); strncpy_s(szAmrFile, MAX_STRING, pBuffer, MAX_STRING - 1); if (isCopy & 0xFF) { env->ReleaseStringUTFChars(jsAmrFile, pBuffer); }