使用JNI总结

一、生成JNI库

1、构建JNI库

2、java命令编译运行多个java文件

假设在B里面引用了A类,编译如下

# 编译
javac -d . A.java B.java
# 运行
java B

二、JNI数据类型

1、固定参数JNIEnv和jobject

2、数据类型说明

基本数据类型对应签名
java类型JNI类型JNI签名
booleanjbooleanZ
bytejbyteB
charjcharC
shortjshortS
intjintI
longjlongJ
floatjfloatF
doublejdoubleD
对象(引用)类型对应签名

对于引用类型签名,以L开头,接类签名,以;结尾,如下:

java类型JNI类型JNI签名说明
StringjstringLjava/lang/String;
Objectjobjectjava中的任何对象
ClassjclassLjava/lang/Class;
ThrowableLjava/lang/Throwable
Object[]jobjectArray[Ljava/lang/Object;任何对象的数组
int[]jintArray[I数组在java是引用类型???
boolean[]jbooleanArray[Z
byte[]jbyteArray[B
char[]jcharArray[C
short[]jshortArray[S
int[]jintArray[I
long[]jlongArray[J
float[]jfloatArray[F
double[]jdoubleArray[D
方法类型签名

对于方法签名描述的转换,首先是将方法内所有参数转换成对应的字段描述,并全部写在小括号内,然后在小括号外再紧跟方法的返回值类型描述;
(参数签名…)返回签名
举例:

java类型JNI签名说明
String f();()Ljava/lang/String;
long f(int i, Class c);(ILjava/lang/Class)J
String(byte[] bytes);([B)Ljava/lang/String

3、使用举例

返回对象

例如JNI要返回Person 的对象

public class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

2种方法,传入对象JNI修改返回(建议),JNI创建对象返回,native如下

//方法1 从Java层传入一个对象
public native Person getPerson(Person person);
//方法2 完全从Native代码中创建对象
public native Person getPerson2();

JNI中找到类,获取(创建)对象,并修改返回

// 第一种方式,传入对象,修改,然后返回
JNIEXPORT jobject JNICALL
Java_com_myapplication_MainActivity_getPerson(JNIEnv *env, jobject instance, jobject person) {
    // 找到对象的Java类
    jclass myClass = env->FindClass("com/myapplication/Person");
    // 获取对应的Java类属性
    jfieldID name = env->GetFieldID(myClass, "name", "Ljava/lang/String;");
    jfieldID age = env->GetFieldID(myClass, "age", "I");
    //属性赋值,person为传入的Java对象
    env->SetObjectField(person, name, env->NewStringUTF("liuwei"));
    env->SetIntField(person, age, 20);
    // 返回对象
    return person;
}
// 第二种方式,JNI内部构建对象,修改然后返回
JNIEXPORT jobject JNICALL
Java_com_myapplication_MainActivity_getPerson2(JNIEnv *env, jobject instance) {
	// 获取对象的类
    jclass myClass = env->FindClass("com/myapplication/Person");
    // 获取类的构造函数,记住这里是调用无参的构造函数
    jmethodID id = env->GetMethodID(myClass, "<init>", "()V");
    // 创建一个新的对象
    jobject person_ = env->NewObject(myClass, id);
    // 获取对象的属性
    jfieldID name = env->GetFieldID(myClass, "name", "Ljava/lang/String;");
    jfieldID age = env->GetFieldID(myClass, "age", "I");
    // 修改对象属性
    env->SetObjectField(person_, name, env->NewStringUTF("liuwei"));
    env->SetIntField(person_, age, 20);
    // 返回对象
    return person_;
}

4、关于JNI内部的内存释放

1、GetStringUTFChars 和 ReleaseStringUTFChars,GetXXArrayElements 和 ReleaseXXArrayElements,必须对应起来,否则会导致内存泄漏。
2、GetXXArrayElements 生成的数据不能在 ReleaseXXArrayElements 之后使用。
3、如果是在JNI函数内通过NewStringUTF、NewXXXArray或NewObject创建的java对象无论是否需要返回java层,都不需要手动释放,jvm会自动管理。但是如果是通过 AttachCurrentThread 创建的 JNIEnv 去New的对象,必须通过 DeleteLocalRef 方式及时删除,因为在线程销毁之前,创建的对象无法自动回收。
4、通过 NewGlobalRef 创建的对象必须手动释放。
5、FindClass 和 GetMethodID 不需要释放。
6、如果不是通过 NewGlobalRef 函数创建的java对象不能跨线程调用,jclass 也是 jobject,如果是在 JNI_OnLoad 创建,那么必须通过 NewGlobalRef 函数处理后才能正常使用。
Android NDK 开发中正确释放 JNI 对象


三、自己编译笔记

# 虚拟机
export PATH=$PATH:/home/zhh/work/open_lib/java_runtime/jdk/jdk-19.0.1/bin
javac -h . YZApi.java ImageQuality.java ImageInfo.java
g++ -std=c++11 -I/home/zhh/work/open_lib/java_runtime/jdk/jdk-19.0.1/include -I/home/zhh/work/open_lib/java_runtime/jdk/jdk-19.0.1/include/linux -I/home/zhh/work/328platsdk/FaceID_PC_SDK_centos7/include -shared -o libxxx.so yzsdk_YTApi.cpp ../../lib/faceid.cpp -fPIC -L/home/zhh/work/328platsdk/testsdk/lib/cpu -llombo
export LD_LIBRARY_PATH=/home/zhh/work/328platsdk/testsdk/javaapi/yzsdk:/home/zhh/work/328platsdk/testsdk/lib:/home/zhh/work/328platsdk/testsdk/lib/cpu
export LIBRARY_PATH=/home/zhh/work/328platsdk/testsdk/lib/cpu
export FACEIDSDKPATH=/home/zhh/work/328platsdk/testsdk/lib/models

# 服务器
g++ -std=c++11 -I/opt/bcpfdev/jdk/jdk1.8.0_221/include -I/opt/bcpfdev/jdk/jdk1.8.0_221/include/linux -I/opt/yz/FaceID_PC_SDK_centos7/include -shared -o libxxx.so yzsdk_YTApi.cpp ../../lib/faceid.cpp -fPIC -L/opt/yz/FaceID_PC_SDK_centos7/lib/cpu -llombo
g++ -std=c++11 -I/opt/bcpfdev/jdk/jdk1.8.0_221/include -I/opt/bcpfdev/jdk/jdk1.8.0_221/include/linux -I/opt/yz/FaceID_PC_SDK_centos7/include -shared -o libxxx.so yzsdk_YTApi.cpp ../../lib/faceid.cpp -fPIC
/opt/yz/cp/jdk-19.0.1/bin/javac -d . testjava.javac ../../javaapi/yzsdk/ImageQuality.java ../../javaapi/yzsdk/ImageInfo.java ../../javaapi/yzsdk/YZApi.java

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/yz/cp/testsdk/javaapi/yzsdk:/opt/yz/FaceID_PC_SDK_centos7/build2:/opt/yz/FaceID_PC_SDK_centos7/lib/cpu
/opt/yz/cp/jdk-19.0.1/bin/java testjava
export FACEIDSDKPATH=/opt/yz/cp/testsdk/lib

JNI完全手册,转载
Java Native Interface(JNI)从零开始详细教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值