NDK开发:JNI基础

作者: 小村医 | 来源:发表于2019-09-18 21:48 被阅读0次

    JNI(Java Native Interface)
    Java调用C/C++,C/C++调用Java的一套API

    JNI开发流程:

    1. 编写native方法
    2. javah命令,生成.h头文件
    3. 复制.h头文件到CPP工程中
    4. 实现.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变量。

    1. 局部引用:函数执行完后会自动回收,也可以通过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);
            
        }
    }
    
    1. 全局引用
    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!");
        }
    }
    

    相关文章

      网友评论

        本文标题:NDK开发:JNI基础

        本文链接:https://www.haomeiwen.com/subject/vtypyctx.html