JNI 概要

目录

JNI Interface Functions and Pointers

java类的Native 方法

Global reference and Local reference

访问primitive array

Exception

 一些基本的访问java函数

参考文档


JNI(Java Native Interface), 通过JNI 函数可以允许运行的java 虚拟机中的代码与其他语言代码交互。

JNI Interface Functions and Pointers

native代码通过JNI function 去访问java VM的功能。 JNI function 通过指针去调用访问。 所有的JNI function 指针被组织在一个array中, array中的每一个元素就是一个JNI function的指针。Interface pointer 就是指向该array的指针,该指针指向一个array of pointers, array中的每个pointer 指向一个JNI function。 每个JNI function 在该array 中有一个固定的偏移量。在调用natvie function是,Interface pointer 作为native函数的第一个参数。 

java类的Native 方法名和参数

在java类中声明native function, 如下例,声明native 函数f, 并通过System.loadLibrary加载f所在的库。

package pkg;

class Cls {
    native double f(int i, String s);
    static {
        System.loadLibrary("pkg_Cls");
    }
}

 java类中声明的native functin 的名字,并不是跟native code中的function名字相同。

关于java中的native function name 与native 库中方法名的匹配,jni specification 中做了说明, 但是也可以调RegisterNatives()来指定对应的native方法名。具体可查阅参考文档。

在jni specification 中,说明了java中的native function name的转化规则。

  • 增加"Java_"前缀。
  • 然后增加全包名的前缀, 包名中的"/"转为“_”。
  • 加下划线"_"。
  • java 类中的native方法名。
  • 加双下划线"__", 然后跟参数signature。

例如下例中的f 函数在native code中名字为Java_pkg_Cls_f__ILjava_lang_String_2:

package pkg;

class Cls {
    native double f(int i, String s);
}

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */

每一个native functin 都自动会增加两个参数:

  • JNIEnv *env env 为JNI interface pointer, 为指向所有JNI function 的指针的列表。
  • jobject obj,obj为呼叫该native function 的java 类。 

Global reference and Local reference

所有传入native 的基本类型变量都是copy, 而传入的java object 都是reference。

reference 分为local reference 和global reference. 垃圾回收器通过reference知道某个java object 被native代码持有, 从而判断是否能释放相应的object。

Local reference 是指native 函数执行期间持有的reference. 函数执行结束后,自动释放,这依赖于java vm的自动实现。 一般所有jni函数的object参数和返回值都是local reference。  local reference 只在创建的thread中有效, 不能跨thread传递。

某些情况需要手动释放local reference, 防止占用过多内存,以便GC可以回收。可在不再继续使用后调用DeleteLocalRef()手动free。

  • 有大size的object的local reference.
  • native 代码中持有大量的local reference。

global reference 则是程序一直持有,必须手动显示释放, 在native代码中可以将一个local reference 转化为global reference。使用NewGlobalRef and DeleteGlobalRef可以创建和删除global reference。 

访问primitive array

natvie code中遍历java array 效率较低,所以JNI 设计了pinning 概念,可以直接获得array 元素指针,该机制要求array memory 必须连续并且jvm GC支持pinning技术。因此在实现上,设计了一系列的JNI函数,例如Get<PrimitiveType>ArrayElements 系列函数, 这些函数对于小的存储在连续memory中的array 直接返回该momery, 或者会copy array 内容到连续的memory中, 并返回该memory指针。

Exception

JNI 通过return code 和exception 来汇报异常,natvie code 也可以提交exception 给java 代码。

一般,如果return code是正常值, 则一定没有exception. 只有如下两种情况例外:

  1. native 代码调用java function, 并返回java function 的值,在java function中产生exception。
  2. 一些jni array function 并不返回error code, 但可以抛出ArrayIndexOutOfBoundsException or ArrayStoreException。

Asynchronous Exceptions

如果当前thread运行调用JNI API, 产生的excetion 不是JNI api自身产生的,而是jvm 自身内部的错误比如StackOverflowError or OutOfMemoryError, 属于Asynchronous Exceptions 范畴。Asynchronous Exceptions 并不影响当前thread的运行, 直到:

  • native code 呼叫一个可以产生synchronous exceptions 的JNI function.
  • native code 使用 ExceptionOccurred() 去检查synchronous and asynchronous exceptions.

如果有exception, native 代码要么自己处理exception并使用ExceptionClear()清除exception, 要么立即返回到呼叫的java 函数中,使exception 被java 函数处理。 

  • 一旦遇到exception, 在清理该exception前, 不能呼叫任何jni function. 但是一下function 可以在处理一个pending exception是调用:
  • ExceptionOccurred()
    ExceptionDescribe()
    ExceptionClear()
    ExceptionCheck()
    ReleaseStringChars()
    ReleaseStringUTFChars()
    ReleaseStringCritical()
    Release<Type>ArrayElements()
    ReleasePrimitiveArrayCritical()
    DeleteLocalRef()
    DeleteGlobalRef()
    DeleteWeakGlobalRef()
    MonitorExit()
    PushLocalFrame()
    PopLocalFrame()
    DetachCurrentThread()

 一些基本的访问java函数

(全部函数可查阅参考文档中的JNI specification:  https://docs.oracle.com/javase/10/docs/specs/jni/index.html)

  1. 找到所要访问的属性或方法所属的类
jclass cls = (*env)->FindClass(env, "java/lang/String;");

      2. 获得属性或方法的ID

jfieldID fieldID = (*env)->GetFieldID(env, cls, "mField", "I");
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

3.访问属性或调用方法

Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
int value = (*env)->GetIntField(env, instance, fieldID);
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);
(*env)->SetIntField(env, instance, fieldID, 10);

 访问类属性, static:

NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);

参考文档

java specification: https://docs.oracle.com/javase/10/docs/specs/jni/index.html

比较全面的blog: https://blog.csdn.net/lonelyroamer/article/details/7958407

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值