JNI是Java本地接口。它定义了管理代码的方法并与本地代码互动,直接的说就是java与C/C++的互动。咱们应该认真的了解一下j2se 6上的JNI。了解它的功能。更详细的信息能够访问Jva官网。 java
JNI定义了两个关键的数据结构,JavaVM和JNIEnv。本质上来讲你能够吧它们理解为函数表的指针。JavaVM 提供调用接口来建立和销毁它本身,从理论上来讲每一个进程容许有多个JavaVMs ,但在android上只容许一个。JNIEnv提供大部分JNI函数。你的本地函数都接收一个JNIEnv 作为第一个参数。 android
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来链接线程,前提是这些线程在退出以前咱们就要执行调用。 jvm
若是你想要从本地代码访问一个对象的值域(或者叫属性),你应该这么作: 函数
一样的,若是你想调用一个方法,首先你得到一个类对象的引用而后得到一个方法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会更快。
一、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();
二、使用GetStringUTFChars()以后要注意释放内存
使用上述函数以后,要使用ReleaseStringUTFChars()函数释放字符串内存。
示例代码:
pBuffer = env->GetStringUTFChars(jsAmrFile, &isCopy); strncpy_s(szAmrFile, MAX_STRING, pBuffer, MAX_STRING - 1); if (isCopy & 0xFF) { env->ReleaseStringUTFChars(jsAmrFile, pBuffer); }