JNI介绍

JNI

JNI是Java本地接口。它定义了管理代码的方法并与本地代码互动,直接的说就是java与C/C++的互动。咱们应该认真的了解一下j2se 6上的JNI。了解它的功能。更详细的信息能够访问Jva官网。 java

JavaVM 和JNIEnv

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 参数是一个很差的作法。 数据结构

Threads

全部线程都是Linux线程,并由内核调度。它们一般从托管代码开始(例如 Thread.start),但它们也能从别处建立而后链接到JavaVM。例如,一个线程使用pthread_create建立,而后被JNI 的AttachCurrentThread 或AttachCurrentThreadAsDaemon 函数来链接。在一个线程被链接以前,它没有JNIEnv就不能使用JNI调用 。 多线程

Android 不会挂起正在执行本地代码的线程。若是Gc(垃圾回收)正在处理,或者debugger 发出一个挂起请求,android将会在下一次暂停这个线程并使用一个JNI调用。 异步

另外咱们须要经过JNI调用DetachCurrentThread来链接线程,前提是这些线程在退出以前咱们就要执行调用。 jvm

jclass, jmethodID, jfieldID

若是你想要从本地代码访问一个对象的值域(或者叫属性),你应该这么作: 函数

  • 使用FindClass得到一个类对象的引用
  • 使用GetFieldID得到一个域ID
  • 筛选你须要的域类型,例如使用GetIntField

一样的,若是你想调用一个方法,首先你得到一个类对象的引用而后得到一个方法ID。这些ID每每都是内部运行时数据结构中的指针。寻找他们可能须要几个String的比较,一旦你实际调用过它们,那么得到值域或调用方法是很是快的。 性能

若是性能要求比较高的话,寻找这些值并在你的网本地代码中缓存结果是颇有用的。由于每个进程只有一个JavaVM的限制,在一个静态本地结构存储这些数据是很合理的。 编码

在类被卸载以前,它的引用,值域ID和方法ID是保证有效的。

当一个类被载入时,若是你想要缓存ID,而且想要当类被卸载和重载时自动从新缓存它们,正确的方法就是初始化这些ID并添加下面一段代码:

private static native void nativeInit(); //静态块语法,不熟悉的请先补java  static {
        nativeInit();
    }

请注意上面代码只执行一次,就是当类被第一次初始化时,无论你这个类被new多少次nativeInit()只执行一次,除非你把这个类卸载掉,在从新载入它才会被执行。

UTF-8 and UTF-16 Strings

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);
   }
相关文章
相关标签/搜索