Android JNI 学习(四):接口方法表 & Base Api & Exception Api
本文我们来总结一下JNI 提供的功能列表及相关的函数表。
注意:请注意使用术语“必须”来描述对JNI程序员的限制。例如,当您看到某个JNI函数必须接收非NULL对象时,您有责任确保不将NULL传递给该JNI函数。因此,JNI实现不需要在该JNI函数中执行NULL指针检查。
一、接口方法表
可以通过JNIEnv参数以固定偏移量访问每个函数。JNIEnv的类型是一个指向存储所有JNI函数指针的结构。它的定义如下:
typedef const struct JNINativeInterface * JNIEnv;
VM初始化功能表,如下面的代码所示 。请注意,前三个条目是保留用于将来与COM的兼容性的。此外,我们NULL
在函数表的开头附近保留了许多附加条目,因此,例如,可以在FindClass之后而不是在表的末尾添加与类相关的未来JNI操作。
const struct JNINativeInterface ... = { NULL,
NULL,
NULL,
NULL,
GetVersion, DefineClass,
FindClass, FromReflectedMethod,
FromReflectedField,
ToReflectedMethod, GetSuperclass,
IsAssignableFrom, ToReflectedField, Throw,
ThrowNew,
ExceptionOccurred,
ExceptionDescribe,
ExceptionClear,
FatalError, PushLocalFrame,
PopLocalFrame, NewGlobalRef,
DeleteGlobalRef,
DeleteLocalRef,
IsSameObject,
NewLocalRef,
EnsureLocalCapacity, AllocObject,
NewObject,
NewObjectV,
NewObjectA, GetObjectClass,
IsInstanceOf, GetMethodID, CallObjectMethod,
CallObjectMethodV,
CallObjectMethodA,
CallBooleanMethod,
CallBooleanMethodV,
CallBooleanMethodA,
CallByteMethod,
CallByteMethodV,
CallByteMethodA,
CallCharMethod,
CallCharMethodV,
CallCharMethodA,
CallShortMethod,
CallShortMethodV,
CallShortMethodA,
CallIntMethod,
CallIntMethodV,
CallIntMethodA,
CallLongMethod,
CallLongMethodV,
CallLongMethodA,
CallFloatMethod,
CallFloatMethodV,
CallFloatMethodA,
CallDoubleMethod,
CallDoubleMethodV,
CallDoubleMethodA,
CallVoidMethod,
CallVoidMethodV,
CallVoidMethodA, CallNonvirtualObjectMethod,
CallNonvirtualObjectMethodV,
CallNonvirtualObjectMethodA,
CallNonvirtualBooleanMethod,
CallNonvirtualBooleanMethodV,
CallNonvirtualBooleanMethodA,
CallNonvirtualByteMethod,
CallNonvirtualByteMethodV,
CallNonvirtualByteMethodA,
CallNonvirtualCharMethod,
CallNonvirtualCharMethodV,
CallNonvirtualCharMethodA,
CallNonvirtualShortMethod,
CallNonvirtualShortMethodV,
CallNonvirtualShortMethodA,
CallNonvirtualIntMethod,
CallNonvirtualIntMethodV,
CallNonvirtualIntMethodA,
CallNonvirtualLongMethod,
CallNonvirtualLongMethodV,
CallNonvirtualLongMethodA,
CallNonvirtualFloatMethod,
CallNonvirtualFloatMethodV,
CallNonvirtualFloatMethodA,
CallNonvirtualDoubleMethod,
CallNonvirtualDoubleMethodV,
CallNonvirtualDoubleMethodA,
CallNonvirtualVoidMethod,
CallNonvirtualVoidMethodV,
CallNonvirtualVoidMethodA, GetFieldID, GetObjectField,
GetBooleanField,
GetByteField,
GetCharField,
GetShortField,
GetIntField,
GetLongField,
GetFloatField,
GetDoubleField,
SetObjectField,
SetBooleanField,
SetByteField,
SetCharField,
SetShortField,
SetIntField,
SetLongField,
SetFloatField,
SetDoubleField, GetStaticMethodID, CallStaticObjectMethod,
CallStaticObjectMethodV,
CallStaticObjectMethodA,
CallStaticBooleanMethod,
CallStaticBooleanMethodV,
CallStaticBooleanMethodA,
CallStaticByteMethod,
CallStaticByteMethodV,
CallStaticByteMethodA,
CallStaticCharMethod,
CallStaticCharMethodV,
CallStaticCharMethodA,
CallStaticShortMethod,
CallStaticShortMethodV,
CallStaticShortMethodA,
CallStaticIntMethod,
CallStaticIntMethodV,
CallStaticIntMethodA,
CallStaticLongMethod,
CallStaticLongMethodV,
CallStaticLongMethodA,
CallStaticFloatMethod,
CallStaticFloatMethodV,
CallStaticFloatMethodA,
CallStaticDoubleMethod,
CallStaticDoubleMethodV,
CallStaticDoubleMethodA,
CallStaticVoidMethod,
CallStaticVoidMethodV,
CallStaticVoidMethodA, GetStaticFieldID, GetStaticObjectField,
GetStaticBooleanField,
GetStaticByteField,
GetStaticCharField,
GetStaticShortField,
GetStaticIntField,
GetStaticLongField,
GetStaticFloatField,
GetStaticDoubleField, SetStaticObjectField,
SetStaticBooleanField,
SetStaticByteField,
SetStaticCharField,
SetStaticShortField,
SetStaticIntField,
SetStaticLongField,
SetStaticFloatField,
SetStaticDoubleField, NewString, GetStringLength,
GetStringChars,
ReleaseStringChars, NewStringUTF,
GetStringUTFLength,
GetStringUTFChars,
ReleaseStringUTFChars, GetArrayLength, NewObjectArray,
GetObjectArrayElement,
SetObjectArrayElement, NewBooleanArray,
NewByteArray,
NewCharArray,
NewShortArray,
NewIntArray,
NewLongArray,
NewFloatArray,
NewDoubleArray, GetBooleanArrayElements,
GetByteArrayElements,
GetCharArrayElements,
GetShortArrayElements,
GetIntArrayElements,
GetLongArrayElements,
GetFloatArrayElements,
GetDoubleArrayElements, ReleaseBooleanArrayElements,
ReleaseByteArrayElements,
ReleaseCharArrayElements,
ReleaseShortArrayElements,
ReleaseIntArrayElements,
ReleaseLongArrayElements,
ReleaseFloatArrayElements,
ReleaseDoubleArrayElements, GetBooleanArrayRegion,
GetByteArrayRegion,
GetCharArrayRegion,
GetShortArrayRegion,
GetIntArrayRegion,
GetLongArrayRegion,
GetFloatArrayRegion,
GetDoubleArrayRegion,
SetBooleanArrayRegion,
SetByteArrayRegion,
SetCharArrayRegion,
SetShortArrayRegion,
SetIntArrayRegion,
SetLongArrayRegion,
SetFloatArrayRegion,
SetDoubleArrayRegion, RegisterNatives,
UnregisterNatives, MonitorEnter,
MonitorExit, GetJavaVM, GetStringRegion,
GetStringUTFRegion, GetPrimitiveArrayCritical,
ReleasePrimitiveArrayCritical, GetStringCritical,
ReleaseStringCritical, NewWeakGlobalRef,
DeleteWeakGlobalRef, ExceptionCheck, NewDirectByteBuffer,
GetDirectBufferAddress,
GetDirectBufferCapacity, GetObjectRefType
};
请注意,函数表可以在所有JNI接口指针之间共享。
二、JNI 接口基本方法(JNI Base Api)
1. GetVersion (获取版本信息)
jint GetVersion(JNIEnv *env);
返回JNI的版本号。
参数:
env: jni接口指针
返回值:
返回一个值,其中高位为major版本号返回,低位为minor版本号。
在 JDK/JRE 1.1中返回 0x00010001
在 JDK/JRE 1.2中返回 0x00010002
在 JDK/JRE 1.4中返回 0x00010004
在 JDK/JRE 1.6中返回 0x00010006
常量:
SINCE JDK/JRE 1.2:
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
/* Error codes */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error
SINCE JDK/JRE 1.4:
#define JNI_VERSION_1_4 0x00010004
SINCE JDK/JRE 1.6:
#define JNI_VERSION_1_6 0x00010006
使用实例:
jint version = env->GetVersion();
2. FindClass(发现Java类)
jclass FindClass(JNIEnv *env, const char *name);
在JDK 1.1发行版中,这个函数加载本地定义的类(locally-defined class), 它搜索在由环境变量 CLASSPATH 目录下的子目录和zip文件中搜索指定的类名。
从JDK 1.2发行版之后,Java安全模型允许非本地系统类也可以加载和调用本地方法。FindClass 函数会使用与当前本地方法关联的ClassLoader, 并用它来加载本地方法指定的class。如果本地方法属于系统类(system class),则没有ClassLoader会被调用。否则,将使用正确的ClassLoader来加载(load)和链接(link)指定名称的类。
从JDK 1.2发行版之后,当通过 Invocation 接口来调用 FindClass 函数,将没有当前的本地方法或与之关联的ClassLoader。这种情况下,会使用 ClassLoader.getSystemClassLoader 来替代。这个ClassLoader 是虚拟机用来创建应用(applications)的,它有能力定位到 java.class.path 参数下的所有类。
第二个 name 参数,使用全称类名或数组类型签名(array type signature)。例如,String类的全称类名为:
"java/lang/String"
而Object数组类型的使用:
"[Ljava/lang/Object;"
注意:一定要注意分隔符不是 . 而是 / ,不要写错了。
参数:
env:JNI接口指针
name:全称的类名(包名以 / 作为分隔符, 然后紧跟着类名),如果名字以 [开头(数组签名标识符),则返回一个数组的类,这个字符串也是MUTF-8。
返回值:
指定名称的类的对象(a class object),或者在没有找到对应类时返回 NULL
抛出异常:
ClassFormatError :如果class内容不是一个有效的class文件。
ClassCircularityError:如果class或interface是它自己的父类或父接口,造成循环层级关系。
OutOfMemoryError:如果系统在载入的过程中内存不足。
NoClassDefFoundError:如果指定的类或接口没有被找到。(当name传null或超长时也会抛出这个异常)
使用实例:
// Start thread which receives commands from the SA.
jclass threadClass = env->FindClass("java/lang/Thread");
if (threadClass == NULL) stop("Unable to find class java/lang/Thread");
jstring threadName = env->NewStringUTF("Serviceability Agent Command Thread");
if (threadName == NULL) stop("Unable to allocate debug thread name");
jmethodID ctor = env->GetMethodID(threadClass, "<init>", "(Ljava/lang/String;)V");
if (ctor == NULL) stop("Unable to find appropriate constructor for java/lang/Thread");
// Allocate thread object
jthread thr = (jthread) env->NewObject(threadClass, ctor, threadName);
以上代码来之 openJDK 源码 中的 /hotspot/agent/src/share/native/jvmdi/sa.cpp
3. GetSuperclass (获取父类)
jclass GetSuperclass(JNIEnv *env, jclass clazz);
只要传入的 clazz 参数不是 java/lang/Object 则返回该类的父类。
如果传入的 clazz 参数是 java/lang/Object 则返回NULL,因为它没有父类。当传入的是一个接口,而不是类时,也返回 NULL 。
参数:
env:JNI接口指针
clazz: Java类对象(java class object)
返回值:
返回传入的 clazz 的父类,或 NULL .
使用实例:
jclass clazz = env->GetObjectClass(thiz);
clazz = env->GetSuperclass(clazz);
jfieldID __state = env->GetFieldID(clazz, "__state", "J");
以上代码来至:https://github.com/liucheng98/mesos-0.22.0/blob/5cfd2c36c0e8361fa2e2d9b6f191a738d22a9cfd/src/java/jni/org_apache_mesos_state_LogState.cpp
4. IsAssignableFrom (检查类是否能转型)
jboolean IsAssignableFrom(JNIEnv *env, jclass class1, jclass clazz2);
检查 clazz1 的对象是否能被安全的转型(cast)为 clazz2
参数:
env:JNI接口指针
clazz1:第一个class参数(需要转型的类)
clazz2:第二个class参数(转型的目标类)
返回值:
如果是以下情况则返回 JNI_TRUE :
clazz1 和 clazz2 指向同一个java类
clazz1 是 clazz2 的子类。(向上转型是安全的)
clazz1 是 clazz2(接口)的实现类。(也属于向上转型)
使用实例:
jclass listInterface = env->FindClass("java/util/List")
jclass arrayListClass = env->FindClass("java/util/ArrayList")
jboolean isSafe = env->IsAssignableFrom(arrayListClass, listInterface);
// isSafe: true;
isSafe = env->IsAssignableFrom(listInterface, arrayListClass);
// isSafe: false;
三、 JNI 异常方法 (JNI Exception)
1. Throw (抛出异常)
jint Throw(JNIEnv *env, jthrowable obj);
触发一个 java.lang.Throwable 对象的异常被抛出。
参数:
env:JNI接口指针
obj: java.lang.Throwable 对象
返回值:
成功则返回0, 失败时返回赋值
抛出异常:
抛出 java.lang.Throwable 对象
使用实例:
jthrowable exception = env->ExceptionOccurred();
if (exception) {
env->ExceptionClear();
detach_internal(env, this_obj);
env->Throw(exception);
return;
}
以上代码来至 OpenJDK 源码 /hotspot/agent/src/os/solaris/proc/saproc.cpp
2. ThrowNew (构造一个异常对象并抛出)
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
Exception对象的构造器函数,message为异常的错误消息,clazz为异常的类。
参数:
env:JNI接口指针
clazz: java.lang.Throwable 的子类
message: 用于创建 java.lang.Throwable 对象时传入的错误消息。这个是字符串是MUTF-8编码。
返回值:
成功则返回0, 失败时返回赋值
抛出异常:
抛出刚构造出来的 java.lang.Throwable 对象
使用实例:
env->ThrowNew(env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"), errMsg);
3. ExceptionOccurred (检查是否有异常被抛出)
jthrowable ExceptionOccurred(JNIEnv *env);
检查是否有异常被抛出。这个异常在本地方法调用 ExceptionClear() 方法或被Java代码处理这个异常之前都会保持在被抛出状态。
参数:
env:JNI接口指针
返回值:
返回过程中抛出的异常,或没有异常被抛出时返回 NULL 。
使用实例:
jthrowable exception = env->ExceptionOccurred();
if (exception) {
env->ExceptionClear();
detach_internal(env, this_obj);
env->Throw(exception);
return;
}
以上代码来至 OpenJDK 源码 /hotspot/agent/src/os/solaris/proc/saproc.cpp
4. ExceptionDescribe (打印异常的stack trace)
void ExceptionDescribe(JNIEnv *env);
打印一个异常的stack trace到系统的错误输出,例如 stderr 这是为了调试提供便利。
参数:
env:JNI接口指针
使用实例:
jthrowable exc = safe_ExceptionOccurred(env);
if (exc) {
env->DeleteLocalRef(exc);
env->ExceptionDescribe();
env->ExceptionClear();
}
以上代码来至:OpenJDK 源码中的 /jdk/src/windows/native/sun/windows/awt_Object.cpp
5. ExceptionClear (清理所有即将抛出的异常)
void ExceptionClear(JNIEnv *env);
清理任何即将抛出的异常。如果没有异常被抛出,则不起任何作用。
参数:
env:JNI接口指针
使用实例:
jthrowable exc = safe_ExceptionOccurred(env);
if (exc) {
env->DeleteLocalRef(exc);
env->ExceptionDescribe();
env->ExceptionClear();
}
以上代码来至:OpenJDK 源码中的 /jdk/src/windows/native/sun/windows/awt_Object.cpp
6. FatalError (抛出一个严重的错误)
void FatalError(JNIEnv *env, const char *msg);
抛出一个严重错误,并不希望虚拟机恢复。
参数:
env:JNI接口指针
msg : 错误消息,这个字符串为MUTF-8编码
使用实例:
env->CallVoidMethod(currentThread, setThreadName, threadName) ;
if( env->ExceptionCheck() ) {
env->ExceptionDescribe() ;
env->FatalError("setting thread name failed: could not start reading thread");
return 0L ;
}
以上代码来至:https://github.com/tnarnold/netsnmpj/blob/3153c3e9297dd7de7db9d1a65c97f77937ae147b/netsnmpj-preliminary/native/nativeThread.cc
7. ExceptionCheck (快速检查是否有异常被抛出)
jboolean ExceptionCheck(JNIEnv *env);
这是一个快速函数用于检查是否有被抛出的异常,而不创建一个这个异常的局部引用。
参数:
env:JNI接口指针
返回值:
有一个即将被抛出的异常时返回 JNI_TURE ,没有则返回 JNI_FALSE
使用实例:
env->CallVoidMethod(thread, runId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
// handle exception
}
以上代码来至:OpenJDK 源码中的 /jdk/src/windows/native/sun/windows/awt_Toolkit.cpp
Android JNI 学习(四):接口方法表 & Base Api & Exception Api的更多相关文章
- Android JNI学习(四)——JNI的常用方法的中文API
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI学习(三)——Java与Native相互调用
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI学习(五)——Demo演示
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI学习(二)——实战JNI之“hello world”
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Spring Boot 项目学习 (四) Spring Boot整合Swagger2自动生成API文档
0 引言 在做服务端开发的时候,难免会涉及到API 接口文档的编写,可以经历过手写API 文档的过程,就会发现,一个自动生成API文档可以提高多少的效率. 以下列举几个手写API 文档的痛点: 文档需 ...
- Android JNI 学习(一):JNI 简介
JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作. ...
- Android JNI 学习(二):JNI 设计机制
本章我们重点说明以下JNI设计的问题,本章中提到的大多数设计问题都与native方法有关.至于调用相关的API的设计,我们会在后面进行介绍. 一.JNI接口函数和指针 native 代码通过调用JNI ...
- android JNI常用添加log方法
android JNI 打log方法 添加库支持 LOCAL_LDLIBS :=-llog -landroid 包含头文件 #include <android/log.h> #define ...
- 技术转载:Jni学习四:如何编写jni方法
转载:http://blog.chinaunix.net/u1/38994/showart_1099528.html 一.概述: 在这篇文章中将会简单介绍如何编制一些简单的JNI 方法.我们都知道JN ...
随机推荐
- hanjiaqi
2017*1501:我是韩佳琦:我的爱好是睡觉: 我的码云个人主页是:https://gitee.com/projects/new 我的第一个项目地址是:https://gitee.com/hanji ...
- Python之路(第三十三篇) 网络编程:socketserver深度解析
一.socketserver 模块介绍 socketserver是标准库中的一个高级模块,用于网络客户端与服务器的实现.(version = "0.4") 在python2中写作S ...
- SpringBoot图片上传
毕设终于写到头像上传了,本来想用Vue写来着,但是一直不顺利,还是对Vue用的不太熟.所以就用jquery写了. 首先添加以下标签 <img id="avatarPreview&quo ...
- STS中web.xml配置文件
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" " ...
- mysql数据库保存sesison会话
<?php header('Content-type:text/html;charset=gbk;'); date_default_timezone_set('PRC'); class db{ ...
- 图解HTTP第六章
HTTP 报文 1>HTTP 报文 2>HTTP 请求报文 在请求中,HTTP 报文由方法.URI.HTTP 版本.HTTP 首部字段.报文主体(不一定需要)等部分构成. 3>HTT ...
- (Swiftmailer)高效的PHP邮件发送库
Swiftmailer是一个类似PHPMailer邮件发送组件,它也支持HTML格式.附件发送,但它发送效率相当高,成功率也非常高,很多PHP框架都集成了Swiftmailer. Swiftmaile ...
- C语言内存四区的学习总结(三)---- 栈区
接上篇内存四区的堆区的总结,下面做一些栈区的相关总结. 一.栈区的分析: 就下面测试程序 #include "stdio.h" #include "string.h&qu ...
- 利用canvas实现刮刮乐效果
最近做了个情人节表白的项目,表白内容时被遮盖的,刮开后才能显示,并且刮开一定比例后清空所有遮罩. function guaguale(obj,w,h){//obj时canvas元素 var canva ...
- Linux-3.0.8中基于S5PV210的IRQ模块代码追踪和分析
init/main.c: asmlinkage void start_kernel(void) { ...... early_irq_init(); init_IRQ(); ...... } earl ...