C的预编译
c语言执行的流程
预编译:完成代码文本的替代工作
编译:形成目标代码(.obj)
链接:将目标代码与c函数库连接合并,形成最终的可执行文件
执行:运行
void main() {
#include "my.txt";
getchar();
}
my.txt的内容
printf("%s\n", "I am a great man");
1.JNI定义
1.关键词的解析JNIEXPORT. JNICALL
2.JNIenv. env 是Java运行环境,在c++是一级指针c是二级指针:需要传this改变env的值,c需要传env需要二层,c++有this指针所以用一层就行了。
3.jclass obj是所属调用这个jni方法是对象。当是所属调用方法是在静态类中时是jclass。这个参数可以获取当前调用对象或者类中的属性和方法等。
4.JNIenv 可以通过这个方法创建类变量,通过放射获取java对象等待。
5.Java传递的参数在JNIenv 和Jclass 后面带着。基本类型有一个转化标具体看转化标。结构类型的话有String object 数组(基本类型数组,对象类型数组)等。
2.c++调Java,Java调c++
JNIenv->findClass 查找Java层的对象。
JNIenv->GetMethodID 获取Java对象中的方法。
JNIenv-> GetFieldID (JNIenv env, jclass clazz, const char *name, const char *sig)获取Java中的属性.设置是否可以访问
获取Java对象中的方法执行.
调用父类的值方法执行等。
1.jni处理基本类型
基本数据传进来也是在JNIenv 和jclass后面传进来的。
基本类型对照表.
访问静态属性
// 访问静态属性
JNIEXPORT jstring JNICALL 方法名称(JNIEnv *env, jobject jobj){
// jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
// jfieldID 获取基本类型的静态 int count变量
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
// GetStatic<Type>Field
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
// 修改静态java 变量的值
(*env)->SetStaticIntField(env, cls, fid, count);
}
C访问Java的方法
//访问java方法
JNIEXPORT 返回值 JNICALL 方法名称(JNIEnv *env, jobject jobj){
// jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
// jmethodID GetMethodID 获取非静态方法
// GetStaticMethodID 获取静态的方法
jmethodID mid = (*env)->GetMethodID(env, cls, "方法名称", "参数和返回值");
// Call<Type>Method 获取方法的值
int random = (*env)->CallIntMethod(env, jobj, mid, 200);
}
2.jni处理对象
1.构建任何想要的对象. 要求获取Java类的构造方法
例子:获取当前对象的构造方法
class cls = (*env)->FindClass(env, "java/util/Date");
// jmethodID
jmethod construct_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
// 实例化一个Date对象
jobject date_obj = (*env)->NewObect(env, cls, construct_mid);
// 从对象中获取时间的方法
jmethod mid = (*env)->GetMehodID(env, cls, "getTime", "()J");
// 调用Java对象中的方法获取时间数据
jlong time = (*env)->CallLongMethod(ENV, date_obj, mid);
获取对象父类的方法
// 获取传进来的jobj对象的类
class cls = (*env)->GetObjectClass(env, jobj);
// 获取对象中的属性
jfieldID fid = (*env)->GetFieldID(env, cls, "属性名", “对象签名”);
// 获取属性对象的obj
jobject j_obj = (*env)->GetObjectField(env, jobj, fid);
// 获取属性对象的类. 如果是子类传子类的包名和类名,如果是父类的包名传父类的包名和类名
jclass j_cls = (*env)->FindClass(env, "对象包名和类名");
// 获取属性类中的方法id
jmethodID j_mid = (*env)->GetMethodID(env, j_cls, "方法名称", "V()");
// 执行对象中的方法
(*env)->CallObjectMethod(env, j_obj, j_mid);
// 执行父类的方法
(*env)->CallNonvirtualObjectMethod(env, j_obj, j_cls, j_mid);
3.jni处理字符串
获取当前调用的Java对象的类
// 获取当前调用的Java对象的class
class cls = (*env)->GetObjectClass(env, jobj);
获取属性名称,属性签名
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String");
通过属性签名,获取jstring的属性
// 获取jclass 中的的字符串
jstring str = (*env)->GetObjectField(env, job, fid);
通过jstring获取char* c字符串,JNI_TRUE表示是否是复制 key和str 指向同一个字符串地址,c_str这个指针是指向同一个还是需要重新划分一个地址出来
char *c_str = (*env)->GetStringUTFChars(env, jstr, JNI_TRUE);
将C字符串转化成jstring
jstring new_jstr = (*env)->NewStringUTF(env, text);
修改调用方法所在对象中的属性的值
(*env)->SetObjectField(env, job, fid, new_jstr);
c中处理字符串的有拼接修改等
strcat拼接
jni中文乱码处理
// 将Java传进来的string转化成char数组
char *c_str = (*env)->GetStringUTFChars(env, inStr, JNI_FALSE);
char c_str[] = "C字符串"
//C中的中文字符串传到Java处理。可以使用Java的string方法来处理
//获取String对象,通过string对象中的方法处理
//1.执行String的构造函数
//2.获取jmethodID
//3.byte数组
//4.字符编码
// 获取String的class
class str_cls = (*env)->FindClass(env, "java/lang/String");
// 获取字符转化的方法
jmethodID constructor_mid = (*env)->GetMethodID(env, j_str, "<init>", "([BLjava/lang/String;])V");
//jbyte -> char
//jbyteArray->char[]
jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
//byte 数组赋值
//0->strlen(c_str) 从0开始赋值到结束.c_str这个字符数组复制到bytes这个字符数组中
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
//字符编码jstring
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
//调用构造函数,返回编码之后的字符串
jstring jstr = (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
4.jni处理数组
int compare(int *a, int *b){
return (*a) - (*b);
}
JNIEXPORT 返回值 JNICALL 方法名称(JNIEnv *env, jobject jobj, jintArray arr){
// jintArray->jint指针-> C int 数组.最后一个参数为ture表示复制一个新的数组出来修改,NULL表示同步 false也表示同步
jint *elems = (*env)->GetIntArrayElementd(env, arr, NULL);
// 排序
qsort(elms, len, sizeof(jint), compare);
// 同步
// 第四个参数mode的意思
// 0:Java数组进行更新,并且释放C/c++数组
// JNI_ABORT:对Java数组不更新,并且释放C/c++数组
// JNI_COMMIT:对Java数组更新,不释放c/c++数组(函数执行完后)
(*env)->ReleaseIntArrayElements(env, arr, elems, 0);
}
返回数组:
//返回数组
JNIEXPORT 返回数据 JNICALL 方法名称(JNIenv *env, jobject obj, jint len){
// 创建一个指定大小的数组
jintArray jint_arr = (*env)->NewIntArray(env, len);
jint *elems = (*env)->GetIntArrayElements(env, int_arr, NULL);
int i = 0;
for (; i<len; i++) {
elems[i] = i;
}
// 同步
(*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);
return jint_arr;
}
五 JNI引用变量 作用告诉虚拟机何时回收一个jni变量,提高效率减少定义jni变量
1.局部引用 通过DeleteLocalref手动释放对象
访问了一个很大的java对象,在使用之后还进行了复杂的耗时操作
创建了大量的局部引用,占用了太多的内存,而且这些对象需要释放的
//局部引用
JINEXPORT jintArray JNICALL 方法名称(JNIEnv *env, jobject jobj){
int i = 0;
for (; i < 5; i++) {
// 创建Date对象
jclass cls = (*env)->FindClass(env, "java/util/Date");
// 获取结构体的方法id
jmethodID construct_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
// 创建对象
jobject jobj = (*env)->NewObject(ENV, cls,construct_mid);
//使用这个对象
//回收
(*env)->DeleteLocalRef(env, obj);
}
}
2.全局引用
数据共享 提高效率 手动控制对象的释放
//全局引用 在方法外面
jstring global_str;
//创建的方法 给全局变量赋值
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
jstring obj = (*env)->NewStringUTF(env, "jni very nice");
global_str = (*env)->NewGlobalRef(env, obj);
}
//使用的方法 获取全局变量的值
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
return global_str;
}
//释放全局变量占用的内存
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
(*env)->DeleteGlobalRef(env, global_str);
}
3.弱全局引用
//节约内存,在内存不足时自动释放所引用的对象
//可以引用一个不常用的对象,如果为null,临时创建
//创建弱全局引用:NewWeakGlobalRef
六.JNI的异常处理
JNI自己抛出的异常,在Java层无法被捕获,只能在c层处理用户自己通过Java的exception抛出
// 异常处理
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
jcalss cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;");
//检测是否发生了Java异常
jthrowable exception = (*env)->ExceptionOccurred(env);
if (exception != NULL){
//让Java代码可以继续执行
//清空异常信息
(*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, "changsha") != 0) {
// 认为抛出异常, 给java层处理
jcalss newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
(*env)->ThrowNew(env, newExcCls, "key's blue is invalid!");
}
}
七. JNI缓存策略
// 缓存机制
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
jcalss cls = (*env)->GetObjectClass(env, cls, "key", "Ljava/lang/String");
// 获取jfieldID只获取一次
// 局部静态变量
static jfieldID key_id = NULL;
if (key_id == NULL) {
key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String");
}
}
//全局的变量 在动态库加载完成之后,进行全局变量初始化
jfieldID key_fid;
JNIEXPORT void JNICALL 方法名称(JNIEnv *env, jobject jobj){
jcalss cls = (*env)->GetObjectClass(env, cls, "key", "Ljava/lang/String");
// 获取jfieldID只获取一次
if (key_fid == NULL) {
key_fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String");
}
}