NDK开发:JNI基础
2019-09-18 本文已影响0人
小村医
JNI(Java Native Interface)
Java调用C/C++,C/C++调用Java的一套API
JNI开发流程:
- 编写native方法
- javah命令,生成.h头文件
- 复制.h头文件到CPP工程中
- 实现.h头文件中声明的函数
一、JNI数据类型
Java基本数据类型与JNI数据类型的映射关系
Java | JNI | C |
---|---|---|
boolean | jboolean | unsigned char |
byte | jbyte | signed char |
char | jchar | unsigned char |
short | jshort | short |
int | jint | int |
long | jlong | long long |
float | jfloat | float |
double | jdouble | double |
引用类型
java | JNI |
---|---|
String | jstring |
object | jobject |
byte[] | jbyteArray |
object[] | jobjectArray |
java属性与方法签名
数据类型 | 签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
object | L开头,以/分隔包名,后面在加; 比如String签名是Ljava/lang/String |
Array | [开头,再加上数组元素的签名,比如int[],签名是[I,int[][]签名是[[ |
二、C/C++访问Java
C/C++访问Java的成员
//访问属性修改属性key
JNIEXPORT jstring JNICALL Java_com_binzi_jni_Test_accessField
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取jfieldId
jfieldId fid = (*env)->GetFieldID(env, cls, "key", "Ljava.lang/String;")
//获取key属性的值
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
//jstring转成c字符串
char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
//拼接得到的字符串
char text[20] = "add";
strcat(text,c_str);
//将拼接后的c字符串转换成jstring
jstring new_jstr = (*env)->NewStringUTF(env, text);
//修改key
//Set<Type>Field
(*env)->SetObjectField(env, jobj, fid, new_jstr);
}
C/C++访问Java静态属性
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_accessStaticField
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jfieldID
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
//GetStatic<Type>Field
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
//修改
//SetStatic<Type>Field
(*env)->SetStaticIntField(env,cls,fid,count);
}
C/C++访问Java方法
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_accessMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
//调用java函数
//Call<Type>Method
jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
printf("random num:%ld",random);
//.....
}
C/C++访问Java静态方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
//调用静态方法
//CallStatic<Type>Method
jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
//jstring -> char*
//isCopy JNI_FALSE代表java和c操作的是同一个字符串
char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
}
C/C++访问Java构造方法
JNIEXPORT jobject JNICALL Java_com_binzi_jni_test_accessConstructor
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->FindClass(env, "java/util/Date");
//jmethodID
jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//实例化一个data对象
jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
//调用getTime方法
jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()L");
jlong time = (*env)->CallLongMethod(env, date_obj, mid);
printf("time:%lld\n",time);
return date_obj;
}
C/C++访问Java父类方法
JNIEXPORT void JNICALL Java_com_binzi_jni_test_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取human属性对象
jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/binzi/jni/Human;");
jobject human_obj = (*env)->GetObjectField(env, jobj, fid);
//执行Human的方法
jclass human_cls = (*env)->FindClass(env, "com/bizi/jni/Human"); //注意传父类的名称
jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");
//调用sayHi
(*env)->CallObjectMethod(env, human_obj, mid);
//调用父类的sayHi
(*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);
}
JNI 中文乱码
JNIEXPORT jstring JNICALL Java_com_binzi_jni_Test_chineseChars
(JNIEnv *env, jobject jobj, jstring in){
//Êä³ö
//char *c_str = (*env)->GetStringUTFChars(env, in, JNI_FALSE);
//printf("%s\n",c_str);
//c -> jstring
char *c_str = "中文";
//char c_str[] = "中文"
//1.jmethodID
//2.byte数组
//3.字符编码
//执行String(byte bytes[], String charsetName)构造函数方法
jclass str_cls = (*env)->FindClass(env, "java/lang/String");
jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
//jbyte -> char
//jbyteArray -> char[]
jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
//byte数组赋值
//从c_str字符数组复制到bytes数组
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
//编码jstring
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
//调用构造函数,返回编码后的jstring
return (*env)->NewObject(env,str_cls,constructor_mid,bytes,charsetName);
}
数组操作
int compare(int *a,int *b){
return (*a) - (*b);
}
//数组排序
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_giveArray
(JNIEnv *env, jobject jobj, jintArray arr){
//jintArray -> jint指针 -> c int 数组
jint *elems = (*env)->GetIntArrayElements(env, arr, JNI_FALSE);
//printf("%#x,%#x\n", &elems, &arr);
//数组长度
int len = (*env)->GetArrayLength(env, arr);
//调用c的快速排序
qsort(elems, len, sizeof(jint), compare);
//同步
//0, Java数组进行更新并释放c/c++数组
//JNI_ABORT:Java数组不更新,释放c/c++数组
//JNI_COMMIT:Java数组更新,不释放c/c++数组,函数执行完会释放
(*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);
}
三、JNI引用类型
JNI引用分为局部引用
和全局引用
,在JNI中告知JVM何时回收一个JNI变量。
- 局部引用:函数执行完后会自动回收,也可以通过
DeleteLocalRef
手动释放对象
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_localRef(JNIEnv *env, jobject jobj){
int i = 0;
for (; i < 5; i++){
//创建date对象
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
jobject obj = (*env)->NewObject(env, cls, constructor_mid);
//不再使用obj对象,通知垃圾回收器回收对象
(*env)->DeleteLocalRef(env, obj);
}
}
- 全局引用
jstring global_str;
//创建全局引用
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_createGlobalRef(JNIEnv *env, jobject jobj){
jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!");
global_str = (*env)->NewGlobalRef(env, obj);
}
//获取全局引用
JNIEXPORT jstring JNICALL Java_com_binzi_jni_Test_getGlobalRef(JNIEnv *env, jobject jobj){
return global_str;
}
//释放全局引用
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_deleteGlobalRef(JNIEnv *env, jobject jobj){
(*env)->DeleteGlobalRef(env, global_str);
}
四、异常处理
JNIEXPORT void JNICALL Java_com_binzi_jni_Test_exeception(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;");
//检测是否有异常
jthrowable exception = (*env)->ExceptionOccurred(env);
if (exception != NULL){
//清空异常信息
(*env)->ExceptionClear(env);
fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
}
//获取属性值
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
char *str = (*env)->GetStringUTFChars(env, jstr, NULL);
//对比两个属性是否相等
if (_stricmp(str, "super jason") != 0){
//抛出异常给java层处理
jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
(*env)->ThrowNew(env,newExcCls,"key's value is invalid!");
}
}