jni.h 文件详解

一、 jni.h 文件详解

1 jni.h头文件

Java Native Interface (JNI)头文件路径:java/jdk/include/jni.h

Java Native Interface头文件是Java开发工具包(Java Development Kit,JDK)中的一部分。JNI允许Java代码与其他编程语言(如C或C++)进行交互,通过使用JNI.h头文件,开发者可以访问和使用底层的本地功能和库。

  • 定义了JavaVM、JNIEnv
  • 定义了基本数据类型、数组类型
  • 定义了方法签名公共类型
  • 定义了属性ID、方法ID
  • 定义了 JNINativeInterface 结构体、 JNIEnv 结构体(C++)

1.1 JavaVM、JNIEnv

JNIEnv 概念 : 是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 ;

即在每个线程中都有一个 JNIEnv 指针, 每个JNIEnv 都是线程专有的, 其它线程不能使用本线程中的 JNIEnv。 在同一个线程中, 多次调用 JNI层方法, 传入的 JNIEnv 是相同的;

JNIEnv 结构 : 由上面的代码可以得出, JNIEnv 是一个指针, 指向一个线程相关的结构, 线程相关结构指向 JNI 函数指针 数组, 这个数组中存放了大量的 JNI 函数指针, 这些指针指向了具体的 JNI 函数;

JNIEnv 与 JavaVM : 注意区分这两个概念;

  • JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, 一个JVM中只有一个JavaVM实例,这个实例是线程共享的;

  • JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv;

在 C 语言 和 C++ 中 , JNIEnv 代表着不同的含义 ;

#if defined(__cplusplus)  //声明 C++ 环境下的 JNIEnv 类型 ;
typedef _JNIEnv JNIEnv;   
typedef _JavaVM JavaVM;   				           //将 _JNIEnv 结构体类型声明为 JNIEnv 类型
#else 					  //声明 C 语言环境下的 JNIEnv 类型 ;
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;  //将 JNINativeInterface 结构体指针 类型 声明为 JNIEnv 类型 
#endif

  • C 语言中的 JNIEnv * env 实际上是 JNINativeInterface** 类型。调用其中的方法指针时 , 先解引用得到其一维指针 , 然后调用对应的函数指针: (* env)->函数指针 。

  • C++ 中专门定义了 JNIEnv 结构体类型,JNIEnv结构体的函数直接封装调用了 JNINativeInterface 结构体中的函数指针 , 只需要调用 JNIEnv 结构体中的方法即可。 C++ 中的 JNIEnv * env , 可以将其当做一个对象使用直接调用其中的方法: env->方法名称;

总结 : JNI 中定义的函数指针 , 实际都定义在 JNINativeInterface 结构体中 ;

JNIEnv 的创建和释放
JNIEnv 创建 和 释放 : 定义在 JavaVM结构体中

  • 调用 (AttachCurrentThread)(JavaVM, JNIEnv*, void) 方法, 创建 JNIEnv结构体;

  • 调用 (DetachCurrentThread)(JavaVM)方法, 释放本线程中的 JNIEnv;

1.2 基本数据类型、数组类型的定义



/* jni_md.h contains the machine-dependent typedefs for jbyte, jint and jlong */
typedef long jint;
typedef __int64 jlong;
typedef signed char jbyte;

#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H

typedef unsigned char   jboolean;
typedef unsigned short  jchar;
typedef short           jshort;
typedef float           jfloat;
typedef double          jdouble;

typedef jint            jsize;

#ifdef __cplusplus
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};

typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;

#else

struct _jobject;

typedef struct _jobject *jobject;//代表了Java中包含native方法的类的一个实例
typedef jobject jclass;          //jclass代表的是一个类对象
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;

#endif

1.3 方法签名公共类型的定义

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

//Object对象   L开头,包名/类名,”;”结尾,$标识嵌套类
//数组         [内部类型 如果是普通类型的数组不需要加“;”后缀,如果是Object类型的数组则需要添加”;”

1.4 属性ID、方法ID的定义

struct _jfieldID;
typedef struct _jfieldID *jfieldID;

struct _jmethodID;
typedef struct _jmethodID *jmethodID;

1.5 JNINativeInterface_ 结构体的定义

JNINativeInterface_结构体类型中定义了 229 个函数指针 , 用于处理 Java 层 与 Native 层的数据交互信息

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);

    jclass (JNICALL *DefineClass)
      (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
       jsize len);
    jclass (JNICALL *FindClass)
      (JNIEnv *env, const char *name);

    jmethodID (JNICALL *FromReflectedMethod)
      (JNIEnv *env, jobject method);
    jfieldID (JNICALL *FromReflectedField)
      (JNIEnv *env, jobject field);

    jobject (JNICALL *ToReflectedMethod)
      (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);

    jclass (JNICALL *GetSuperclass)
      (JNIEnv *env, jclass sub);
    jboolean (JNICALL *IsAssignableFrom)
      (JNIEnv *env, jclass sub, jclass sup);

    jobject (JNICALL *ToReflectedField)
      (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);
	
	......
};

1.6 _JNIEnv 结构体

_JNIEnv 结构体中封装了一个 JNINativeInterface 结构体类型指针 , _JNIEnv 结构体中也封装了 229 个 方法 , 每个方法都调用 对应的 JNINativeInterface functions 中的函数指针 ;*

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
#ifdef __cplusplus

    jint GetVersion() {
        return functions->GetVersion(this);
    }
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);
    }
    jmethodID FromReflectedMethod(jobject method) {
        return functions->FromReflectedMethod(this,method);
    }
    jfieldID FromReflectedField(jobject field) {
        return functions->FromReflectedField(this,field);
    }

    jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {
        return functions->ToReflectedMethod(this, cls, methodID, isStatic);
    }

    jclass GetSuperclass(jclass sub) {
        return functions->GetSuperclass(this, sub);
    }
    jboolean IsAssignableFrom(jclass sub, jclass sup) {
        return functions->IsAssignableFrom(this, sub, sup);
    }

    ......
}

二、 Jni函数总结

以下以 JNINativeInterface 结构体中函数为例,在C++中去除第一个参数即可。

2.1 访问Java对象

  • jclass (JNICALL *GetObjectClass) (JNIEnv *env, jobject obj);
    获取对象所属的类,object对隐式this参数对象的引用

  • jclass (JNICALL *FindClass) (JNIEnv *env, const char *name);
    以字符串形式来指定类名

  • jclass (JNICALL *GetSuperClass)(JNIEnv *env,jclass obj)

    通过jclass可以获取其父类的jclass实例。

2.2 访问Java对象的域

  • jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
    返回类中一个域的标识符

  • jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
    返回类中一个静态域的标识符

  • void (JNICALL *SetDoubleField) (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);
    设置域的值

  • jdouble (JNICALL *GetDoubleField) (JNIEnv *env, jobject obj, jfieldID fieldID);
    返回域的double值

  • jint (JNICALL *GetStaticIntField) (JNIEnv *env, jclass clazz, jfieldID fieldID);
    返回域的double值

2.3 调用Java方法

2.3.1 调用实例方法(非静态)

  • j_class class_PrintWriter = (*env)->GetObjectClass(env, object);
    获取隐式参数this的类
  • j_methodID id_print = (*env)->GetMethodID(env, class_PrintWriter, “print”, “(Ljava/lang/String;)V”);
    获取方法ID
  • (*env)->CallVoidMethod(env, object, id_print, str);
    根据返回类型,可以使用不同的CallxxxMethod,该方法从C中调用任何Java方法。

2.3.2 调用静态方法

  • jclass class_System = (*env)->FindClass(env, “java/lang/System”);
    获取指定的类
  • jmethodID id_getProperty = (*env)->GetStaticMethodID(env, class_System, “getProperty”,
    “(Ljava/lang/String;)Ljava/lang/String;”);
    根据名称和描述符获取方法ID
  • jobject obj_ret = (*env)->CallStaticObjectMethod(env, class_System, id_getProperty,(*env)->NewStringUTF(env, “java.class.path”));
    根据类、方法ID和入参调用静态方法

2.3.3 调用构造器

  • jclass class_String = (*env)->FindClass(env, “java/lang/String”);
  • jmethodID id_String = (*env)->GetMethodID(env, class_String, “”, “(Ljava/lang/String;)V”);
    通过指定方法名“”,并指定构造器的方法签名,获得放方法ID
  • jobject obj_String = (*env)->NewObject(env, class_String, id_String, para);
    调用构造器方法构造String对象

2.4 数组处理函数

  • GetArrayLength
    返回数组长度
  • GetObjectArrayElement
    返回数组元素值
  • SetObjectArrayElement
    设置新值
  • GetxxxArrayElements
    生成一个指向Java数组元素的C指针。域类型必须是基本类型。指针不再使用时,必须ReleasexxxArrayElements
  • ReleasexxxArrayElements
    通知JVM GetxxxArrayElements获得的指针不再需要
  • GetxxxArrayRegion
    将Java数组元素复制到C数组,域类型必须是基本类型
  • SetxxxArrayRegion
    将C数组元素复制到Java数组

三、加载、卸载本地方法

JVM提供了一种方式允许程序员在加载动态链接库文件的时候做一些自定义操作。

  • JNI_OnLoad()函数是在动态库被加载时调用。
  • JNI_OnUnload()函数则是在本地库被卸载时调用。

所以这两个函数就是一个本地库最重要的两个管理生命周期的函数。

//依旧定义在JNI头文件:java/jdk/include/jni.h

/* Defined by native libraries. */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved);

JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved);
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要下载jni.h文件,首先你需要一个集成了Java开发环境(JDK)的计算机。JDK包含了在运行Java程序时所需的Java编译器和运行环境。 首先,打开你的浏览器,前往Oracle官方网站(https://www.oracle.com/java/technologies/javase-jdk11-downloads.html)下载JDK的最新版本。选择适合你操作系统的版本(Windows、Mac或Linux),并点击下载链接。 下载完成后,双击安装JDK。按照提示一步一步执行安装向导,选择合适的安装位置和选项。完成安装后,配置环境变量。在Windows操作系统中,你可以打开“控制面板”>“系统和安全”>“系统”>“高级系统设置”,点击“环境变量”按钮,在“系统变量”下找到“Path”变量,编辑该变量并在末尾添加JDK安装路径。在Mac和Linux中,你可以编辑.bash_profile或.bashrc文件,并添加类似于"export PATH=$PATH:JDK安装路径"的语句。 配置完成后,在命令行(Windows:命令提示符或PowerShell,Mac和Linux:终端)中输入"java -version"命令,如果输出了Java的版本信息,说明安装配置成功。 接下来,在浏览器中搜索jni.h文件,并选择可信赖的网站进行下载。通常,你可以在官方文档或开源项目的网站上找到jni.h文件。下载完成后,将jni.h文件复制到你的计算机上的一个目录中,例如你项目的源代码文件夹。 现在你可以在你的项目中包含jni.h文件了。在C或C++源文件中,使用#include <jni.h>语句引入jni.h头文件。这样,你就可以在代码中使用JNIJava Native Interface)相关的函数和数据类型了。 希望这些步骤能帮助你成功下载jni.h文件,并开始使用JNIJava和本地代码之间进行交互。 ### 回答2: 要下载jni.h文件,可以按照以下步骤进行: 第一步:打开网络浏览器,例如Chrome或Firefox等。 第二步:在搜索引擎中搜索"jni.h文件下载"。 第三步:从搜索结果中选择一个可信的网站,如CSDN、GitHub等,访问该网站。 第四步:在网站的搜索框中输入"jni.h",然后点击搜索按钮。 第五步:在搜索结果中找到与jni.h相关的文件,通常是一个压缩包或者是单个头文件,如"jni.h.zip"或"jni.h"。 第六步:点击下载按钮,等待文件下载完成。 第七步:下载完成后,解压缩压缩包(如果是压缩包)。 第八步:在解压缩后的文件中找到jni.h文件,根据自己的需要复制或移动该文件到目标位置。 完成以上步骤后,你就可以获得jni.h文件了。注意,为了避免下载到带有病毒的文件,建议只从可信的网站下载文件,并使用杀毒软件对下载的文件进行检查。 ### 回答3: jni.h文件Java Native Interface (JNI) 的头文件,用于支持Java与本地(C/C++)代码的交互。在进行跨平台开发时,有时需要使用JNI来调用系统级别的函数或者使用C/C++编写高性能的代码。 要下载jni.h文件,可以按以下步骤进行操作: 1. 打开对应的JNI的官方网站或者Java的官方网站,例如Oracle的官方网站。 2. 在网站上搜索jni.h文件,通常可以在开发者资源库或者下载页面中找到。 3. 找到jni.h文件后,通常会提供一个下载链接,点击链接即可开始下载。如无下载链接,可以尝试复制文件的URL链接,并在浏览器中粘贴并下载。 4. 下载完成后,将文件保存到本地安装的Java开发环境的include文件夹中。这个路径可能会因操作系统和Java版本而有所不同,可以在安装文档或者官方文档中查找具体的路径。 5. 确保将jni.h文件正确放置在include文件夹中后,就可以在本地代码中引用该文件,以开始使用JNI。 需要注意的是,jni.h文件JNI开发的基础文件,通过该文件的调用可以使用JNI提供的许多功能,如在Java和C/C++之间传递数据、调用本地库函数等。在使用JNI进行开发时,除了jni.h文件外,还需要理解JNI的相关概念和使用方式,并对C/C++和Java编程有一定的了解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值