JNI C与JAVA互调

函数调用

每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)

1)当native方法为静态方法时:
jclass 代表native方法所属类的class对象(JniTest.class)
2)当native方法为非静态方法时:
jobject 代表native方法所属的对象

下面是C与Java互调的例子程序

返回一个Java的字符串

NewStringUTF

//函数实现
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
    //简单的实现
    //将C的字符串转为一个java字符串
    return (*env)->NewStringUTF(env,"C String");
}

访问属性,修改属性

GetObjectField
SetObjectField

//访问属性,修改属性key
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_accessField
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    //属性名称,属性签名(cmd javap命令获得)
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");   

    //Get<Type>Field
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);  
    printf("jstr:%#x\n",&jstr);

    //jstring -> c字符串
    //isCopy 是否复制(true代表赋值,false不复制,操作同一个内存)
    char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
    //拼接得到新的字符串
    char text[20] = "man";
    strcat(text,c_str);

    //c字符串 ->jstring
    jstring new_jstr = (*env)->NewStringUTF(env, text);
    //修改key
    //Set<Type>Field
    (*env)->SetObjectField(env, jobj, fid, new_jstr);
    printf("new_jstr:%#x\n", &new_jstr);
    return new_jstr;
}

访问静态属性

GetStaticFieldID
GetStaticIntField

JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);
    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);
}

调用方法

CallIntMethod

JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");

    //Call<Type>Method
    jint randomMum = (*env)->CallIntMethod(env, jobj, mid, 100);
    printf("random num:%ld",randomMum);
}

访问静态方法

GetStaticMethodID
CallStaticObjectMethod

JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");

    //CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
    printf("%s",uuid);
}

调用构造方法

(*env)->GetMethodID(env, cls, “”, “()V”)

//使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_dongnaoedu_jni_JniTest_accessConstructor
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //jmethodID <init>表示构造方法
    jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    //实例化一个Date对象
    jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
    //调用getTime方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);
    printf("\ntime:%lld\n",time);
    return date_obj;
}

调用父类方法

在Java中,子类B继承父类A,重写父类的一个方法C后,通过子类B对象调用的方法C是子类B的方法,而不是A的C方法,但是通过JNI可以实现子类B对对A的C方法的调用.

CallNonvirtualObjectMethod

JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/dongnaoedu/jni/Human;");
    //获取子类对象
    jobject human_obj = (*env)->GetObjectField(env, jobj, fid);

    jclass human_cls = (*env)->FindClass(env, "com/dongnaoedu/jni/Human"); //注意:传父类的名称
    jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");

    //执行sayHi方法
    //(*env)->CallObjectMethod(env, human_obj, mid);
    //调用的父类的方法
    (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);
}

C传递一个中文的字符串给JAVA

JAVA默认是使用UTF-16编码的,而C是使用的UTF-8编码,这就涉及到字符的转码问题
String(byte bytes[], String charsetName)

JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_chineseChars
(JNIEnv *env, jobject jobj, jstring in){

    //执行String(byte bytes[], String charsetName)构造方法需要的条件
    //1.jmethodID
    //2.byte数组
    //3.字符编码jstring

    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[]
    //c -> jstring
    char *c_str = "马蓉与宋江";
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
    //byte数组赋值
    //0->strlen(c_str),从头到尾
    //对等于,从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);
}

传入数组并修改(排序)

GetIntArrayElements
ReleaseIntArrayElements

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, NULL);
    //数组的长度
    int len = (*env)->GetArrayLength(env, arr);
    //排序
    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);
}

返回指定大小的数组

ReleaseIntArrayElements


JNIEXPORT jintArray JNICALL Java_com_dongnaoedu_jni_JniTest_getArray(JNIEnv *env, jobject jobj, jint len){

    //创建一个指定大小的数组
    jintArray jint_arr = (*env)->NewIntArray(env, len);
    jint *elems = (*env)->GetIntArrayElements(env, jint_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引用分类

LocalReference:局部引用,通过DeleteLocalRef释放
GlobalReference:全局引用,通过DeleteGlobalRef释放
WeakGlobalReference:弱全局引用,通过DeleteGlobalWeakRef释放

循环创建数组

//模拟:循环创建数组
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_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);

        //不在使用jobject对象了
        //通知垃圾回收器回收这些对象
        (*env)->DeleteLocalRef(env, obj);
    }
}

//全局引用
//共享(可以跨多个线程),手动控制内存使用
jstring global_str;

//创建
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_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_dongnaoedu_jni_JniTest_getGlobalRef(JNIEnv *env, jobject jobj){
    return global_str;
}

//释放
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_deleteGlobalRef(JNIEnv *env, jobject jobj){
    (*env)->DeleteGlobalRef(env, global_str);
}

//弱全局引用
//节省内存,在内存不足时可以是释放所引用的对象
//可以引用一个不常用的对象,如果为NULL,临时创建
//创建:NewWeakGlobalRef,销毁:DeleteGlobalWeakRef

异常处理

异常处理作用:
1.保证Java代码可以运行:

JNI自己抛出的异常,在Java层无法被捕捉,只能在C层清空

2.补救措施保证C代码继续运行

用户通过ThrowNew抛出的异常,可以在Java层捕捉

检测异常,补救,抛出异常

ExceptionOccurred
GetFieldID
ThrowNew

JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_exeception(JNIEnv *env, jobject jobj){

    jclass 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, "super jason") != 0){
        //认为抛出异常,给Java层处理
        jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
        (*env)->ThrowNew(env,newExcCls,"key's value is invalid!");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值