JNI介绍

JNI

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

JavaVM 和JNIEnv

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 参数是一个不好的做法。

Threads

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

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

另外我们需要通过JNI调用DetachCurrentThread来连接线程,前提是这些线程在退出之前我们就要执行调用。

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会更快。



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);
   }


你可能感兴趣的:(JNI介绍)