JNI开发之JNI原理

  在上一篇文章中对JNI简单介绍了,在这篇文章中将对JNI原理进行介绍。本篇文章将以JNI执行环境、JNI数据类型、JNI注册方式、JNI引用、JNI变量共享以及JNI调用方式来介绍JNI原理。

 一、执行环境(Runtime)

  在计算机中,每种编程语言都有一个执行环境(Runtime),执行环境用来解释执行语言的语句。在JNI开发中有两个比较重要与执行环境Runtime相关的变量: JavaVMJNIEnv

  • JavaVM
  Java语言的执行环境是Java虚拟机(JVM),JVM其实是主机环境中的一个进程,每个JVM虚拟机进程在本地环境中都有一个JavaVM结构体,该结构体在创建Java虚拟机的时候被返回的,在JNI中创建JVM函数为JNI_CreateJavaVM,在jni.h头文件中有定义。
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
  在jni.h文件中也定义了JavaVM的数据结构,可以看到JavaVM结构中封装了一些函数指针,这些函数指针主要是对JVM操作的接口,定义如下:
#if defined(__cplusplus)
typedef _JavaVM JavaVM; //C++的JavaVM定义
#else
typedef const struct JNIInvokeInterface* JavaVM; //C的JavaVM定义
#endif

/*
 * JNI invocation interface.
 */
struct JNIInvokeInterface {
    void*       reserved0;//保留
    void*       reserved1;//保留
    void*       reserved2;//保留

    jint        (*DestroyJavaVM)(JavaVM*); // 销毁Java虚拟机并回收资源
    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);//链接到当前Java线程
    jint        (*DetachCurrentThread)(JavaVM*); //从当前Java线程中分离
    jint        (*GetEnv)(JavaVM*, void**, jint);// 获得当前线程的Java运行环境
    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*); // 将当前线程作为守护线程
};

/*
 * C++ version.
 */
struct _JavaVM {
    const struct JNIInvokeInterface* functions;

#if defined(__cplusplus)
    jint DestroyJavaVM()
    { return functions->DestroyJavaVM(this); }
    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThread(this, p_env, thr_args); }
    jint DetachCurrentThread()
    { return functions->DetachCurrentThread(this); }
    jint GetEnv(void** env, jint version)
    { return functions->GetEnv(this, env, version); }
    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};
  从代码可以看到,JNIInvokeInterface结构封装了几个和JVM相关的函数。另外在C和C++中,JavaVM的定义是不相同的,在C中的JavaVM是JNIInvokeInterface类型指针,而在C++语言中,JavaVM是在JNIInvokeInterface指针上进行了一次封装,函数调用时少了一个参数。
   JavaVM是JVM在JNI层的代表,在JNI层中有且仅有一个JavaVM。JavaVM是进程相关的,一个进程只有一个JavaVM。

  • JNIEnv
  JNIEnv是当前线程的执行环境,一个JVM对应一个JavaVM结构,而在一个JVM中可能创建多个Java线程,每个线程都有一个JNIEnv结构,这些JNIEnv结构保存在线程的本地存储中(Thread Local Storage)。JNIEnv是线程相关的,即在每个线程中都有一个JNIEnv指针,每个JNIEnv都是线程专有的,其他线程不能使用本线程中的JNIEnv。
   JNIEnv不能跨线程,只在当前线程中有效。JNIEnv不能在线程之间进行传递,在同一个线程中,多次调用JNI层方法,传入的JNIEnv是相同的。但是一个本地方法可以被不同的Java线程调用, 因此本地方法可以接受不同的JNIEnv。
  JNIEnv有两个作用:一个是调用Java函数,JNIEnv代表当前Java线程的运行环境,通过JNIEnv可以调用Java中的代码;另一个是操作Java对象,Java对象传入JNI层就是jobject对象,需要使用JNIEnv来操作这个Java对象。
  JNIEnv的数据结构是在jni.h文件中定义的,可以看到JNIEnv数据结构也是一个函数表,在本地代码中通过JNIEnv的函数表来操作Java数据或调用Java方法。也就是说,只要在本地代码中拿到了JNIEnv结构,就可以在本地代码中调用Java代码。
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;//C++定义
#else
typedef const struct JNINativeInterface* JNIEnv;//C定义
#endif
/*
 * Table of interface function pointers.
 */
//C JNIEnv定义
struct JNINativeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;
    void*       reserved3;

    jclass      (*FindClass)(JNIEnv*, const char*);
    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
    jfieldID    (*GetStaticFieldID)(JNIEnv*, jclass, const char*,const char*);

   .......
    jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);    //注册本地方法
    jint        (*UnregisterNatives)(JNIEnv*, jclass); //反注册本地方法
    jint        (*GetJavaVM)(JNIEnv*, JavaVM**); //获取对应的JavaVM对象
    ......
}

/*
 * C++ object wrapper. 
 *
 * This is usually overlaid on a C struct whose first element is a
 * JNINativeInterface*.  We rely somewhat on compiler behavior.
 */
//C++的JNIEnv定义
struct _JNIEnv {
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)
  jclass FindClass(const char* name)
    { return functions->FindClass(this, name); }
    .......
}

  从代码可知,JNIEnv和JavaVM类似,也是定义了一些函数指针,通过这些函数可以操作Java对象。JNIEnv在C和C++中定义的方式也不相同,C++对JNINativeInterface指针进行了一次封装,调用时更加方便。
  总的来说, JNI其实就是定义了Java语言和本地语言之间的一种沟通方式,这种沟通方式依赖于JavaVM和JNIEnv结构中定义的函数表,这些函数将Java中的方法调用转换为本地语言的函数调用。
  JNI是JVM实现的一部分,JavaVM是JVM在JNI层的代表,每个Java线程都有一个JNIEnv,代表当前线程的执行环境,他们之间的关系如下图所示:
   



二、JNI数据类型


  当Java与Native语言相互调用时,肯定会涉及到数据的传递。这两者属于不同的编程语言,在数据类型上有很多差异的。例如,在C语言中,int类型的长度取决于平台,char类型为1个字节长度,而在Java语言中,int类型固定为4个字节,而char类型为2个字节。为了使Java语言数据类型与Native语言数据类型匹配,需要借助JNI的数据类型来保证它们两者之间的数据类型和数据空间大小匹配。JNI中定义的一些数据类型有基本数据类型和引用数据类型。

    2.1 JNI基本类型

Java类型
Native类型
JNI类型
描述
boolean
unsigned char
jboolean
无符号8比特
byte
signed char
jbyte
有符号8比特
char
unsigned short
jchar
无符号16比特
short
short
jshort
有符号16比特
int
int
jint
有符号32比特
long
long long
jlong
有符号64比特
float
float
jfloat
32比特
double
double
jdouble
64比特
void
void
void
N/A
  Java类型和JNI类型名称具有一致性,JNI类型的名称在只是在Java类型的基础上添加了一个j,例如,Java的int类型对应的JNI类型为jint,Java的long类型,对应的JNI类型为jlong。JNI的基本类型在jni文件中定义了:

typedef unsigned char   jboolean;       /* unsigned 8 bits */
typedef signed char     jbyte;          /* signed 8 bits */
typedef unsigned short  jchar;          /* unsigned 16 bits */
typedef short           jshort;         /* signed 16 bits */
typedef int             jint;           /* signed 32 bits */
typedef long long       jlong;          /* signed 64 bits */
typedef float           jfloat;         /* 32-bit IEEE 754 */
typedef double          jdouble;        /* 64-bit IEEE 754 */
typedef jint            jsize;

  需要注意的是jchar代表的是Java的char类型,对应于C/C++中的却是unsigned short类型,因为Java中的char类型是两个字节,jchar相当于C/C++的宽字符。如果需要在本地方法中定义一个jchar类型的数据,规范的写法应该是jchar = L'C'。

  实际上,所有带j的JNI类型,都是JNI对应的Java类型,并且JNI的类型接口与本地代码在类型的空间大小上是完全匹配的,而在语言层次上却不一定相同。在本地方法中与JNI接口调用时,要在内部进行转换,必须小心处理。
   

2.2 JNI引用类型

    
  在本地代码中为了访问Java运行环境中的引用类型,在JNI中也定义了一套对应的引用类型,他们的对应关系如下:
                          
  所有的引用类型对应于jobject,java.lang.Class类型对应于jclass,数组类型对应于jarray,java.lang.Throwable类型对应于jthrowable,Object数组对应于jobjectArray。
  JNI引用类型都是以j开头的,与Java中所
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值