JNI笔记

介绍

JNI(Java Native Interface)是 Java 提供的一个接口,用于在 Java 程序中调用非 Java 代码,特别是 C 或 C++ 代码。JNI 使得 Java 程序能够直接与本地代码进行交互,从而提高性能,访问底层操作系统特性,或者利用现有的 C/C++ 库。

  • 调用本地方法:通过 JNI,Java 代码可以调用用 C/C++ 编写的本地方法。
  • 有些性能要求高的操作,Java 本身可能无法满足,这时可以将这些操作用本地代码实现,并通过 JNI 调用,从而提升性能。

简单案例

java调用c/c++时,有一下几个步骤需要经历

  1. 静态代码块定义需要导入的c++动态链接库(用来连接c++方法的,windows后缀是dll,linux是so)
  2. 定义native方法(静态的本地方法)
  3. 编译.h头文件
  4. c++导入.h头文件,重写java写的本地静态方法
  5. 编译c++文件为动态链接库
  6. java代码通过动态链接库运行
public class Study1 {
    static {
        // 加载动态链接库(windows是.dll,linux是so)
        System.loadLibrary("Study1");
    }

    // native方法(静态的本地方法),需要调用的c函数
    public static native int add(int a,int b);


    public static void main(String[] args) {
        System.out.println(add(1,2));
    }
}

编译java文件,获取对应的.class编译文件和.h头文件

    # 执行编译.java文件,获取.h头文件(旧版本java用的是javah,而不是java -h)
    javac -h .\src\ .java文件
    # 如果.java文件编码不是GBK,还需要指定编码格式
    javac -h .\src\ -encoding UTF-8 .\src\Study1.java

重写java的native方法(静态的本地方法),直接重写.h的方法

// 重写java的native方法(静态的本地方法)
JNIEXPORT jint JNICALL Java_Study1_add(JNIEnv *,jclass,jint a,jint b){
    printf("c++代码被调用\n");
    return a+b;
}

编译c++文件,生成动态链接库,得到.dll/.so文件,执行java代码测试结果

g++ -o 输出的.dll文件 -fPIC -shared -I"安装的jdk路径\include\win32" -I"安装的jdk路径\include" 编译的.cpp文件
g++ -o study1.dll -fPIC -shared -I"F:\JAVA\JDK\JDK1.8\include\win32" -I"F:\JAVA\JDK\JDK1.8\include" .\src\Study1.cpp

JNI数据类型

JNI(Java Native Interface)中,Java 和 Native 代码(通常是 C 或 C++)之间通过特定的数据格式进行交互。以下是 JNI 中常见的数据格式及其对应关系:


1. 基本数据类型

Java 类型JNI 类型C/C++ 类型
booleanjbooleanunsigned char(1字节)
bytejbytesigned char(1字节)
charjcharunsigned short(2字节)
shortjshortshort(2字节)
intjintint(4字节)
longjlonglong long(8字节)
floatjfloatfloat(4字节)
doublejdoubledouble(8字节)
voidvoidvoid

2. 引用类型

Java 类型JNI 类型说明
StringjstringJava 字符串
Objectjobject任意 Java 对象
ClassjclassJava 类
ThrowablejthrowableJava 异常对象

3. 数组类型

Java 数组类型JNI 类型C/C++ 类型
boolean[]jbooleanArrayjboolean*
byte[]jbyteArrayjbyte*
char[]jcharArrayjchar*
short[]jshortArrayjshort*
int[]jintArrayjint*
long[]jlongArrayjlong*
float[]jfloatArrayjfloat*
double[]jdoubleArrayjdouble*
Object[]jobjectArrayjobject*

4. JNI 中的特殊类型

JNI 类型说明
jmethodID方法 ID
jfieldID字段 ID
JNIEnv*JNI 环境指针
JavaVM*Java 虚拟机指针

5. 常见数据操作

获取数组元素

jintArray intArray = ...;
jint* elements = env->GetIntArrayElements(intArray, NULL);
// 操作数组元素
env->ReleaseIntArrayElements(intArray, elements, 0);

字符串

jstring str = 指针;//jstring是用来存放java的string内容的指针对象

1.解析字符串

jboolean isCopy; //是否复制一个新的指针,str本身就是一个指针对象
const char* fromJava = env->GetStringUTFChars(jStr, &isCopy); //解析为字符串指针
printf("Java String: %s\n", fromJava);  // 打印字符串,printf会解析指针,不必使用&变量来解析指针

2.释放指针

env->ReleaseStringUTFChars(str, fromJava);  // 释放内存

数组

1.获取数组

// 获取数组指针
jint* a =  env->GetIntArrayElements(arr1,isCopy);

// 获取数组指针(按范围)
jint* b = new jint[3];
env->GetIntArrayRegion(arr2,0,env->GetArrayLength(arr2),b);

// 获取数组长度
env->GetArrayLength(arr1);

2.释放数组

env->ReleaseIntArrayElements(intArray, jint, 0);

引用类型(自定义类)

1.获取引用类型

//1.通过jni参数传递类
JNIEXPORT void JNICALL Java_Study_XX(JNIEnv *env, jclass this_){
    // 获取当前类
    jclass clas = env->GetObjectClass(this_);
}

//2.通过jobject获取类(jobject一般也是通过参数传递)
JNIEXPORT void JNICALL Java_Study_XX(JNIEnv *env, jclass this_,jobject obj){
    // 获取当前类
    jclass clas = env->GetObjectClass(obj);
}

//3.通过路径获取类
jclass clas = env->FindClass("com/demo/Other");

2.静态方法和属性

// 调用静态方法
// 1.先获取静态方法Id
// 格式:GetStaticMethodID(jclass, "静态方法名", 方法签名)
jmethodID staticMethod = env->GetStaticMethodID(clas, "getinfo", "(Lcom/demo/User;)Ljava/lang/String;");
// 2.调用方法,CallStaticXXMethod,XX为返回类型,一般用CallStaticObjectMethod
jobject staticRes = env->CallStaticObjectMethod(jclass, staticMethod, user);
// 或者jstring staticRes = (jstring)env->CallStaticObjectMethod(jclass, staticMethod, user);

// 获取静态属性
// 1. 获取静态字段ID,使用 GetStaticFieldID
// 格式:GetStaticFieldID(jclass, "属性名", 属性签名)
jfieldID staticField = env->GetStaticFieldID(clas, "staticFieldName", "Ljava/lang/String;");
// 2. 获取静态属性值,使用 GetStaticObjectField(对于对象类型)或 GetStatic<Type>Field(对于基本数据类型)
jstring staticFieldValue = (jstring)env->GetStaticObjectField(clas, staticField);

// 设置静态属性
// 1. 获取静态字段ID,使用 GetStaticFieldID
jfieldID staticField = env->GetStaticFieldID(clas, "staticFieldName", "Ljava/lang/String;");
// 2. 设置静态属性值,使用 SetStaticObjectField(对于对象类型)或 SetStatic<Type>Field(对于基本数据类型)
env->SetStaticObjectField(clas, staticField, newStaticValue);

3.非静态方法和属性

// 调用非静态方法
// 1. 获取非静态方法的ID,使用 GetMethodID
// 格式:GetMethodID(jclass, "方法名", 方法签名)
jmethodID method = env->GetMethodID(clas, "getInfo", "()Ljava/lang/String;");、
// 2. 调用非静态方法,使用 CallObjectMethod
// 这里假设返回值是 String 类型,所以使用 CallObjectMethod 并强制转换为 jstring
jstring res = (jstring)env->CallObjectMethod(user, method);

// 设置非静态属性值
// 1. 获取非静态字段ID,使用 GetFieldID
jfieldID field = env->GetFieldID(clas, "name", "Ljava/lang/String;");
// 2. 设置非静态属性值,使用 SetObjectField(对于对象类型)或 Set<Type>Field(对于基本数据类型)
env->SetObjectField(user, field, newFieldValue);

4.签名规则

(<参数签名>)<返回类型签名>

  • 基本类型:
    • Z:boolean
    • B:byte
    • C:char
    • D:double
    • F:float
    • I:int
    • J:long
    • S:short
    • V:void
  • 对象类型(一定要加";"符号)
    • L<包名>/<类名>;:表示对象类型,例如 Lcom/demo/User; 表示 com.demo.User 类型的对象。
    • [:表示数组。例如,[I 表示一个 int 类型的数组,[Ljava/lang/String; 表示一个 String 类型的数组。
//案例
jmethodID staticMethod = env->GetStaticMethodID(userClass, "getStaticInfo", "(Lcom/demo/User;)Ljava/lang/String;");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值