NDK(三):JNIEnv解析

一、概述

JNIEnv(Java Native Interface Environment) 是一个JNI接口指针 (每个线程独有一个 JNIEnv 指针),指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地的方法通过JNI函数来访问JVM中的数据结构,详情如下图:

在这里插入图片描述

关联文章:

参考文章:

二、JNIEnv结构体

我们知道 JNI 方法一般都是使用 JNIEnv 去调用,而 JNIEnv 又是一个指针,所以JNI中有哪些函数,只需要找到 JNIEnv 的实现体就可以了。

struct _JNIEnv;
# C中直接使用JNINativeInterface指针进行操作。
typedef const struct JNINativeInterface* C_JNIEnv; 

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;

从上述代码可以看到,C中直接使用 JNINativeInterface 指针进行操作。在C++文件中是对_JNIEnv 起的一个别名 JNIEnv。

下面我们来看下 _JNIEnv 结构体的定义。

struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)

    jint GetVersion()
    { return functions->GetVersion(this); }

    jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
        jsize bufLen)
    { return functions->DefineClass(this, name, loader, buf, bufLen); }

    jclass FindClass(const char* name)
    { return functions->FindClass(this, name); }
    // ...略
}

通过上面的代码可知,_JNIEnv 内部所有的操作都是委托给JNINativeInterface指针进行操作的,相当于_JNIEnv只是一个代理层。而在C语言中直接使用的是JNINativeInterface指针,这就是JNIEnv在C和C++调用方式不一致的原因。

三、JNINativeInterface结构体

下面我们来分析一下 JNINativeInterface 的结构体,JNINativeInterface 结构体中主要包含如下几类的操作:

  • Class操作
  • 反射操作
  • 对象字段 & 方法操作
  • 类的静态字段 & 静态方法操作
  • 字符串操作
  • 锁操作
  • 数组操作
  • 注册和反注册native方法
  • 异常Exception操作
  • 引用的操作

下文中 JNINativeInterface 内的方法有时会省略一些参数信息,我们可以通过 JNI Functions 官方文档 来查看函数原型。

以 FindClass 函数为例:

struct JNINativeInterface {
	jclass      (*FindClass)(JNIEnv*, const char*);
}

// 函数原型为:
jclass FindClass(JNIEnv *env, const char *name);

详情如下图所示:

在这里插入图片描述

3.1 Class操作

struct JNINativeInterface {
    /*获取当前JNI版本信息:*/
    jint 	(*GetVersion)(JNIEnv *);
	
	// 定义一个类:类是从某个字节数组buf中读取出来的
    jclass  (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize);

    // 查找全限定名为name的类:如String类:"java/lang/String"
    jclass 	(*FindClass)(JNIEnv*, const char*);

	// 获取当前类的父类:通常在使用FindClass获取到类之后,再调用这个函数
    jclass	(*GetSuperclass)(JNIEnv*, jclass);
}

3.2 反射操作

struct JNINativeInterface {
	// 将一个Method对象转换为jmethodID
    jmethodID   (*FromReflectedMethod)(JNIEnv*, jobject);
    jfieldID    (*FromReflectedField)(JNIEnv*, jobject);
    // 通过jmethodID,反射得到Method对象
    jobject     (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
    /* spec doesn't show jboolean parameter */
    jobject     (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
}

3.3 对象字段 & 方法操作

struct JNINativeInterface {
	// 通过指定jclass类名、字段名称、字段类型来获取jfieldID
    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
	
	// ---------- 操作Field -------------
	// 通过类的jobject和jfieldID获取字段的jobject对象。
    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
    jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
    // 8种基本类型字段的获取与赋值。
    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
    jbyte       (*GetByteField)(JNIEnv*, jobject, jfieldID);

	// ---------- 操作Method -------------
	// 通过指定jclass类名、方法名称、方法签名信息来获取jmethodID
	jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
	// 8种基本类型:boolean、byte、char、short、int、long、float、double。
	// ...是可变长度的参数,参数类型相同。
    jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
    // va_list是可变长度的参数,参数类型可以不同。
    jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
    // jvalue 是8中基本类型
    jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);	
}

typedef union jvalue {
    jboolean    z;
    jbyte       b;
    jchar       c;
    jshort      s;
    jint        i;
    jlong       j;
    jfloat      f;
    jdouble     d;
    jobject     l;
} jvalue;

小结:

  • 方法参数通过支持3种不同的参数类型来覆盖所有的参数场景。
  • 单个参数场景:使用 jvalue 来表示支持一个参数的场景。
  • 多个相同参数场景:使用 … 来表示支持同类型的多个参数。
  • 多个不同参数场景:使用 va_list 来表示支持不同类型的多个参数。

3.4 类的静态字段 & 静态方法操作

struct JNINativeInterface {
	// ---------- 操作 Static Field -------------                        
	jfieldID    (*GetStaticFieldID)(JNIEnv*, jclass, const char*, const char*);
	
    void        (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
    jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
    
    void        (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
    jbyte       (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
    
	// ---------- 操作 Static Method -------------
	// 与GetMethodID方法类似。
	jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
	// 三种参数方式:...、va_list、jvalue。
    jbyte       (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
    jbyte       (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
    jbyte       (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
}

3.5 字符串操作

struct JNINativeInterface {
    jstring     (*NewString)(JNIEnv*, const jchar*, jsize);
    jsize       (*GetStringLength)(JNIEnv*, jstring);
    const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
    void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
    jstring     (*NewStringUTF)(JNIEnv*, const char*);
    jsize       (*GetStringUTFLength)(JNIEnv*, jstring);
    /* JNI spec says this returns const jbyte*, but that's inconsistent */
    const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
    void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
}

3.6 锁操作

struct JNINativeInterface {
    jint        (*MonitorEnter)(JNIEnv*, jobject);
    jint        (*MonitorExit)(JNIEnv*, jobject);
}

3.7 数组操作

struct JNINativeInterface {
    jbyteArray  (*NewByteArray)(JNIEnv*, jsize);
    jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
    void        (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint);
                        
    void        (*SetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);
    void        (*GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
}

3.8 注册和反注册native方法

struct JNINativeInterface {
    jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
    jint        (*UnregisterNatives)(JNIEnv*, jclass);
}

动态注册JNI代码时会使用 RegisterNatives 函数。具体请参考 - NDK(五):JNI静态注册与动态注册

3.9 异常Exception操作

struct JNINativeInterface {
    jint        (*Throw)(JNIEnv*, jthrowable);
    jint        (*ThrowNew)(JNIEnv *, jclass, const char *);
    jthrowable  (*ExceptionOccurred)(JNIEnv*);
    void        (*ExceptionDescribe)(JNIEnv*);
    void        (*ExceptionClear)(JNIEnv*);
    void        (*FatalError)(JNIEnv*, const char*);
}

3.10 引用的操作

struct JNINativeInterface {
	// 全局变量的创建与删除
    jobject     (*NewGlobalRef)(JNIEnv*, jobject);
    void        (*DeleteGlobalRef)(JNIEnv*, jobject);
    // 局部变量的创建与删除
    jobject     (*NewLocalRef)(JNIEnv*, jobject);
    void        (*DeleteLocalRef)(JNIEnv*, jobject);
    // 对象的比较
    jboolean    (*IsSameObject)(JNIEnv*, jobject, jobject);


}

3.11 其它

struct JNINativeInterface {
    jint        (*GetJavaVM)(JNIEnv*, JavaVM**);
    jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
    
    jint        (*PushLocalFrame)(JNIEnv*, jint);
    jobject     (*PopLocalFrame)(JNIEnv*, jobject);
}

四、小结

  • JNIEnv 是一个代理,实际的操作全部委托给 JNINativeInterface 指针执行。
  • JNINativeInterface 结构体中主要包含如下几类的操作:
    • Class操作
    • 反射操作
    • 对象字段 & 方法操作
    • 类的静态字段 & 静态方法操作
    • 字符串操作
    • 锁操作
    • 数组操作
    • 注册和反注册native方法
    • 异常Exception操作
    • 引用的操作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值