C++基础二 JNI基础操作

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");
    }
}

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MatrixData

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值