一、生成JNI库
1、构建JNI库
2、java命令编译运行多个java文件
假设在B里面引用了A类,编译如下
# 编译
javac -d . A.java B.java
# 运行
java B
二、JNI数据类型
1、固定参数JNIEnv和jobject
2、数据类型说明
基本数据类型对应签名
java类型 | JNI类型 | JNI签名 |
---|---|---|
boolean | jboolean | Z |
byte | jbyte | B |
char | jchar | C |
short | jshort | S |
int | jint | I |
long | jlong | J |
float | jfloat | F |
double | jdouble | D |
对象(引用)类型对应签名
对于引用类型签名,以L开头,接类签名,以;结尾,如下:
java类型 | JNI类型 | JNI签名 | 说明 |
---|---|---|---|
String | jstring | Ljava/lang/String; | |
Object | jobject | java中的任何对象 | |
Class | jclass | Ljava/lang/Class; | |
Throwable | Ljava/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