基础JNI语法和常见使用

基础JNI语法

基础类型

Java类型native类型描述
booleanjbooleanunsigned 8 bits
bytejbytesigned 8 bits
charjcharunsigned 16 bits
shortjshortsigned 16 bits
intjintsigned 32 bits
longjlongsigned 64 bits
floatjfloat32 bits
doublejdouble64 bits
voidvoidN/A

引用类型

JNI为不同的java对象提供了不同的引用类型,JNI引用类型如下:

在c里面,所有JNI引用类型其实都是jobject。

Native方法参数

  • JNI接口指针是native方法的第一个参数,JNI接口指针的类型是JNIEnv。
  • 第二个参数取决于native method是否静态方法,如果是非静态方法,那么第二个参数是对对象的引用,如果是静态方法,则第二个参数是对它的class类的引用
  • 剩下的参数跟Java方法参数一一对应
extern "C" /* specify the C calling convention */
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

     JNIEnv *env,        /* interface pointer */

     jobject obj,        /* "this" pointer */

     jint i,             /* argument #1 */

     jstring s)          /* argument #2 */
{

     const char *str = env->GetStringUTFChars(s, 0);

     ...

     env->ReleaseStringUTFChars(s, str);

     return ...

}

点击查看JNI接口

签名描述

基础数据类型

Java类型签名描述
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
void

引用数据类型

(以L开头,以;结束,中间对应的是该类型的完整路径)

String : Ljava/lang/String;
Object : Ljava/lang/Object;
自定义类型 Area : Lcom/xuexiang/jnidemo/Area;

数组

(在类型前面添加[,几维数组就在前面添加几个[)

int [] :[I
Long[][]  : [[J
Object[][][] : [[[Ljava/lang/Object

使用命令查看

javap -s <java类的class文件路径>

class文件存在于 build->intermediates->classes下。

在这里插入图片描述

JNI常见用法

1、jni访问java非静态成员变量

  • 1.使用GetObjectClassFindClass获取调用对象的类

  • 2.使用GetFieldID获取字段的ID。这里需要传入字段类型的签名描述。

  • 3.使用GetIntFieldGetObjectField等方法,获取字段的值。使用SetIntFieldSetObjectField等方法,设置字段的值。

注意:即使字段是private也照样可以正常访问。

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallNoStaticField(JNIEnv *env, jobject instance) {
    //获取jclass
    jclass j_class = env->GetObjectClass(instance);
    //获取jfieldID
    jfieldID j_fid = env->GetFieldID(j_class, "noStaticField", "I");
    //获取java成员变量int值
    jint j_int = env->GetIntField(instance, j_fid);
    LOGI("noStaticField==%d", j_int);//noStaticField==0

    //Set<Type>Field    修改noStaticKeyValue的值改为666
    env->SetIntField(instance, j_fid, 666);
}

2、jni访问java静态成员变量

  • 1.使用GetObjectClassFindClass获取调用对象的类

  • 2.使用GetStaticFieldID获取字段的ID。这里需要传入字段类型的签名描述。

  • 3.使用GetStaticIntFieldGetStaticObjectField等方法,获取字段的值。使用SetStaticIntFieldSetStaticObjectField等方法,设置字段的值。

3、jni调用java非静态成员方法

  • 1.使用GetObjectClassFindClass获取调用对象的类

  • 2.使用GetMethodID获取方法的ID。这里需要传入方法的签名描述。

  • 3.使用CallVoidMethod执行无返回值的方法,使用CallIntMethodCallBooleanMethod等执行有返回值的方法。

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallParamMethod(JNIEnv *env, jobject instance) {
    //回调JNIApi中的noParamMethod
    jclass clazz = env->FindClass("com/xuexiang/jnidemo/JNIApi");
    if (clazz == NULL) {
        printf("find class Error");
        return;
    }
    jmethodID id = env->GetMethodID(clazz, "paramMethod", "(I)V");
    if (id == NULL) {
        printf("find method Error");
        return;
    }
    env->CallVoidMethod(instance, id, ++number);
}

4、jni调用java静态成员方法

  • 1.使用GetObjectClassFindClass获取调用对象的类

  • 2.使用GetStaticMethodID获取方法的ID。这里需要传入方法的签名描述。

  • 3.使用CallStaticVoidMethod执行无返回值的方法,使用CallStaticIntMethodCallStaticBooleanMethod等执行有返回值的方法。

5、jni调用java构造方法

  • 1.使用FindClass获取需要构造的类

  • 2.使用GetMethodID获取构造方法的ID。方法名为<init>, 这里需要传入方法的签名描述。

  • 3.使用NewObject执行创建对象。

extern "C"
JNIEXPORT jint JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallConstructorMethod(JNIEnv *env, jobject instance) {
    //获取jclass
    jclass j_class = env->FindClass("com/xuexiang/jnidemo/Area");
    //找到构造方法jmethodID   public Area(int width, int height)
    jmethodID j_constructor_methoid = env->GetMethodID(j_class, "<init>", "(II)V");
    //初始化java类构造方法  public Area(int width, int height)
    jobject j_Area_obj = env->NewObject(j_class, j_constructor_methoid, 2, 10);

    //找到getArea()  jmethodID
    jmethodID j_getArea_methoid = env->GetMethodID(j_class, "getArea", "()I");
    //调用java中的   public int getArea() 获取面积
    jint j_area = env->CallIntMethod(j_Area_obj, j_getArea_methoid);
    LOGI("面积==%d", j_area);//面积==20
    return j_area;
}

6、jni引用全局变量

  • 使用NewGlobalRef创建全局引用,使用NewLocalRef创建局部引用。

  • 局部引用,通过DeleteLocalRef手动释放对象;全局引用,通过DeleteGlobalRef手动释放对象。

  • 引用不主动释放会导致内存泄漏。

7、jni异常处理

  • 使用ExceptionOccurred进行异常的检测。注意,这里只能检测java异常。

  • 使用ExceptionClear进行异常的清除。

  • 使用ThrowNew来上抛异常。

注意,ExceptionOccurredExceptionClear一般是成对出现的,类似于java的try-catch。

//上抛java异常
void throwException(JNIEnv *env, const char *message) {
    jclass newExcCls = env->FindClass("java/lang/Exception");
    env->ThrowNew(newExcCls, message);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_jniTryCatchException(JNIEnv *env, jobject instance) {

    //获取jclass
    jclass j_class = env->GetObjectClass(instance);
    //获取jfieldID
    jfieldID j_fid = env->GetFieldID(j_class, "method", "Ljava/lang/String666;");

    //检测是否发生Java异常
    jthrowable exception = env->ExceptionOccurred();
    if (exception != NULL) {
        LOGE("jni发生异常");
        //jni清空异常信息
        env->ExceptionClear(); //需要和ExceptionOccurred方法成对出现
        throwException(env, "native出错!");
    }
}

相关链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xuexiangjys

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

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

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

打赏作者

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

抵扣说明:

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

余额充值