★01.介绍

Java平台与宿主环境

JNI的角色

使用JNI的潜在风险

使用JNI的场景

 

★02.入门

简介

流程

  1. 创建一个HelloWorld.java文件并声明原生方法。
  2. 使用 javac 编译HelloWorld.java,生成HelloWorld.class文件。
  3. 使用 javah 生成C头文件HelloWorld.h,该头文件包含了原生函数实现的原型。
  4. 新建文件HelloWorld.c并按照HelloWorld.h中声明的原生函数原型实现原生代码。
  5. HelloWorld.c构建成为一个原生库,生成HelloWorld.dll或者HelloWorld.so
  6. 使用 Java 运行HelloWorld程序,HelloWorld.classHelloWorld.dll/so会在运行时被加载。

 

img

JNI Hello World 示意图

HelloWorld示例

1. 创建HelloWorld.java

代码

解说

2. 生成HelloWorld.class

3. 使用CLion创建C/C++项目

4. 生成HelloWorld.h

1. 配置 IDEA 中生成头文件规则(已配置则跳过)

  1. File -> Settings -> External Tools。

  2. 点击“+” 按钮。

    1. Name : 生成头文件
    2. Group : JNI
    3. Options : 全部勾选。
    4. Show in : 全部勾选。
    5. Program : $JDKPath$\bin\javah.exe
    6. Parameters : -jni -v -d $FileDir$ $FileClass$$FileDir$表示输出目录,将$FileDir$设置为 CLion 项目目录。
    7. Working directory : $SourcepathEntry$
  3. 点击确定。

2. 生成

  1. 选择对应的java文件。
  2. 右键 -> JNI -> Generate Header File。
  3. $FileDir$对应的目录里生成了HelloWorld.h

3. 解说HelloWorld.h文件

代码

解说

5. 实现HelloWorld.c

5. 生成动态链接库并运行

 

 

★03.一个简单的Native方法

Prompt.java

Prompt.c

解说

类型映射

其他

 

 

★04.访问Strings

函数表

JNI 函数描述加入版本
Get/ReleaseStringChars获取或者释放一个Unicode格式的字符串,可能返回原始字符串的拷贝JDK1.1
Get/ReleaseStringUTFChars获取或者释放一个UTF-8格式的字符串,可能返回原始字符串的拷贝JDK1.1
GetStringLength返回Unicode字符串的字符个数JDK1.1
GetStringUTFLength返回用于表示某个UTF-8字符串所需要的字节个数(不包括结束的0)JDK1.1
NewString创建一个java.lang.String对象,该对象与指定的Unicode字符串具有相同的字符序列JDK1.1
NewStringUTF创建一个java.lang.String对象,该对象与指定的UTF-8字符串具有相同的字符序列JDK1.1
Get/ReleaseStringCritical获取或者释放一个Unicode格式的字符串的内容,可能返回原始字符串的拷贝,在Get/ReleaseStringCritical之间的代码必须不能阻塞Java2 SDK1.2
Get/SetStringRegion将一个字符串拷贝到预先开辟的空间,或者从一个预先开辟的空间复制字符串,字符使用Unicode编码Java2 SDK1.2
Get/SetStringUTFRegion将一个字符串拷贝到预先开辟的空间,或者从一个预先开辟的空间复制字符串,字符使用UTF-8编码Java2 SDK1.2

jstring

简单示例

函数解说

转换成为Native Strings

释放Native Strings资源

创建新的Strings

获取Strings字符个数

Unicode String相关的其他JNI函数

其它

获取Strings字节个数

异常

 

 

 

★05.访问Arrays

基础类型数组 与 对象数组

基础类型数组

函数表

JNI函数描述加入版本
GetArrayRegion复制基础类型数组的内容到C缓冲区JDK1.1
SetArrayRegion将C缓冲区的内容设置到基础类型数组中去JDK1.1
GetArrayElements获取指向基础类型数组内容的指针,可能返回原始数组内容的拷贝JDK1.1
ReleaseArrayElements释放GetArrayElements获取的指向基础类型数组内容的指针JDK1.1
GetArrayLength返回数组中元素的个数JDK1.1
NewArray创建指定长度的数组JDK1.1
GetPrimitiveArrayCritical获取基础类型数组的内容,可能禁止垃圾回收,可能返回原始数组的一份拷贝Java 2 SDK 1.2
ReleasePrimitiveArrayCritical释放GetPrimitiveArrayCritical获取的基础类型数组的内容Java 2 SDK 1.2

简单示例:Get<Type>ArrayRegion()

简单示例:Get<Type>ArrayElements()

函数解说

函数选择策略

对象数组

简单示例

函数解说

 

 

★06.访问属性

实例属性 与 静态属性

实例属性

简单示例

示例解说

函数解说

静态属性

简单示例

示例解说

函数解说

 

 

★07.调用方法

简介

访问普通方法

简单示例

示例解说

  1. GetObjectClass():通过jobject获取jclass
  2. GetMethodID():通过jclass方法名方法描述符 获取jmethodID。如果没有找到对应方法会抛出NoSuchMethodError方法描述符 的获取方法可以参考 ★06.访问属性
  3. CallVoidMethod():通过jobjectjmethodID调用方法。CallVoidMethod()用于调用返回void类型的方法。

函数解说

访问静态方法

简单示例

示例解说

  1. GetObjectClass():通过jobject获取jclass
  2. GetMethodID():通过jclass方法名方法描述符 获取jmethodID。如果没有找到对应方法会抛出NoSuchMethodError方法描述符 的获取方法可以参考 ★06.访问属性
  3. CallStaticVoidMethod():通过jobjectjmethodID调用方法。CallVoidMethod()用于调用返回void类型的方法。

函数解说

调用基类方法

调用构造函数

简单示例

示例解说

  1. FindClass():通过 类型描述符 获取对应的jclass
  2. GetMethodID():通过jclass"<init>"方法描述符 获取构造函数的jmethodID
  3. NewObject():通过jclass、构造函数jmethodID和构造函数 参数 来调用构造函数创建对象。

函数解说

注意事项

 

 

★08.缓存属性和方法ID

简介

用时缓存

简单示例

示例解说

优缺点

类静态初始化器缓存

简单示例

示例解说

优缺点

两种方法的性能比较

概念

Java/native调用

native/Java调用

 

 

★09.局部引用与全局引用

简介

局部引用

简介

手动释放局部引用

管理局部引用

函数解说

EnsureLocalCapacity()示例

PushLocalFrame() / PopLocalFrame()示例

NewLocalRef()示例

全局引用

弱全局引用

比较引用

管理引用的规则

 

 

★10.异常处理

简述

简单示例

示例解说

函数解说

异常检查

第一种

简介

简单示例

第二种

简介

简单示例

异常处理

效用函数里的异常

相关工具函数

用于便捷抛出异常

通用调用函数

代码

使用例子