Android开发Android开发经验谈Android技术知识

NDK开发:JNI基础

2019-09-18  本文已影响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!");
    }
}
上一篇下一篇

猜你喜欢

热点阅读