基础JNI语法和常见使用

基础JNI语法

基础类型

Java类型 native类型 描述
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void N/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类型 签名描述
boolean Z
byte B
char C
short S
int I
long J
float F
double D
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出错!");
    }
}

相关链接

发布了61 篇原创文章 · 获赞 103 · 访问量 11万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 点我我会动 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览