JNI的基本类型
在JNI中有一些基本类型,这些基本类型只能在JNI层使用
序号 |
属性名 |
java层对应的类型 |
1 |
jobject |
Object |
2 |
jboolean |
boolean |
3 |
jbyte |
byte |
4 |
jchar |
char |
5 |
jshort |
short |
6 |
jint |
Object |
7 |
jlong |
long |
8 |
jfloat |
float |
9 |
jdouble |
double |
10 |
void |
void |
由于下面的函数名都会包含这些基本属性,我这这里标记为<type>,代表了以上的类型
java类型的签名
序号 |
属性名 |
java层对应的类型 |
1 |
Z |
boolean |
2 |
B |
byte |
3 |
C |
char |
4 |
S |
short |
5 |
I |
int |
6 |
J |
long |
7 |
F |
float |
8 |
D |
boolean |
9 |
L |
类 |
10 |
[B |
type[] |
关于变量的操作
序号 |
方法名 |
备注 |
1 |
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) |
根据变量名以及变量签名获得参数ID |
2 |
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig) |
根据变量名以及变量签名获得静态参数ID |
3 |
<type> Get<type>Field(jobject obj, jfieldID fieldID) |
根据fieldID获取变量 |
4 |
void Set<type>Field(jobject obj, jfieldID fieldID, <type>value) |
根据fieldID设置变量值为value |
5 |
<type> GetStatic<type>Field(jobject obj, jfieldID fieldID) |
根据fieldID获取静态变量<type> |
6 |
void SetStatic<type>Field(jobject obj, jfieldID fieldID, <type>value) |
根据fieldID设置静态变量值为value |
类的操作
序号 |
方法名 |
备注 |
1 |
jclass FindClass(JNIEnv *env, const char *name); |
name:类的全名。使用UTF-8编码,其中分隔符使用/表示。return:java类对象。发生错误时返回NULL。 |
2 |
jclass GetSuperclass(JNIEnv *env, jclass clazz); |
clazz:需要查询的类对象。return:clazz的父类类对象。如果clazz是Object类对象或者clazz是接口的类信息,返回NULL。 |
3 |
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,jclass clazz2); |
当我们将一个对象从一种类型转换为另一种类型之前,我们必须确保这种类型转换是安全的。我们可以通过如下方法去判断这两种类型是否可以互相转换。 clazz1:原始类型。class2:目标类型。return:当前类型转换是否安全。 |
4 |
jclass DefineClass(JNIEnv *env, const char *name, jobject loader,const jbyte *buf, jsize bufLen); |
name:类的全名,必须是被UTF-8编码过的字符串。loader:类加载器buf:包含类数据的缓冲区。这个缓冲区包含一个原始类数据,并且要求这个类在调用此方法前没有被JVM所引用。bufLen:缓冲区长度。return:java类对象。发生错误时返回NULL。 |
创建对象的基本操作
序号 |
方法名 |
备注 |
1 |
jobject AllocObject(JNIEnv *env, jclass clazz); |
返回使用clazz类创建的对象,如果clazz没有默认的构造方法,则返回NULL。 |
2 |
jobject NewObject(JNIEnv *env, jclass clazz,jmethodID methodID, …); |
...代表着传入构造参数 |
3 |
jobject NewObjectA(JNIEnv *env, jclass clazz,jmethodID methodID, const jvalue *args); |
args:这里需要传入参数数组 |
4 |
jobject NewObjectV(JNIEnv *env, jclass clazz,jmethodID methodID, va_list args); |
args:指向变参列表的指针 |
5 |
jclass GetObjectClass(JNIEnv *env, jobject obj); |
通过java层传过来的对象创建jObject |
6 |
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj); |
通过如下方法可以得到当前对象的引用类型是全局引用、局部引用还是弱全局引用。 |
typedef enum jobjectRefType {
JNIInvalidRefType = 0,//无效引用
JNILocalRefType = 1,//局部引用
JNIGlobalRefType = 2,//全局引用
JNIWeakGlobalRefType = 3//弱全局引用
} jobjectRefType;
其他关于对象的一些方法
序号 |
方法名 |
备注 |
1 |
jboolean IsInstanceOf(JNIEnv *env, jobject obj,jclass clazz); |
在Java中我们使用instanceof来判断一个对象是否是一个类的实例,在JNI中,需要通过如下方法来进行实例运算。 |
2 |
jboolean IsSameObject(JNIEnv *env, jobject ref1,jobject ref2); |
在Java中使用==可以判断两个引用是否指向同一个对象,在JNI中使用下列方法可以进行相同的判断,无论是全局引用,局部引用还是弱全局引用。 |
关于方法的操作
序号 |
方法名 |
备注 |
1 |
jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig); |
根据方法名以及方法签名获得方法ID |
2 |
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) |
根据方法名以及方法签名获得静态方法ID |
3 |
<type> Call<type>Method(jobject obj, jmethodID methodID, ...) |
根据methodID及传入方法变量执行方法 |
4 |
<type> Call<type>MethodV(jobject obj, jmethodID methodID, va_list args) |
根据methodID及传入方法变量指针执行方法 |
5 |
<type> Call<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args) |
根据methodID及传入方法变量数组执行方法 |
6 |
<type> CallNonvirtual<type>Method(jobject obj, jmethodID methodID, ...) |
根据methodID及传入方法变量执行非虚实例方法 |
7 |
<type> CallNonvirtual<type>MethodV(jobject obj, jmethodID methodID, va_list args) |
根据methodID及传入方法变量指针执行非虚实例方法 |
8 |
<type> CallNonvirtual<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args) |
根据methodID及传入方法变量数组执行非虚实例方法 |
9 |
<type> CallStatic<type>Method(jobject obj, jmethodID methodID, ...) |
根据methodID及传入方法变量执行静态方法 |
10 |
<type> CallStatic<type>MethodV(jobject obj, jmethodID methodID, va_list args) |
根据methodID及传入方法变量指针执行静态方法 |
11 |
<type> CallStatic<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args) |
根据methodID及传入方法变量数组执行静态方法 |
12 |
<type> CallStaticNonvirtual<type>Method(jobject obj, jmethodID methodID, ...) |
根据methodID及传入方法变量执行静态非虚实例方法 |
13 |
<type> CallStaticNonvirtual<type>MethodV(jobject obj, jmethodID methodID, va_list args) |
根据methodID及传入方法变量指针执行静态非虚实例方法 |
14 |
<type> CallStaticNonvirtual<type>MethodA(jobject obj, jmethodID methodID, const jvalue* args) |
根据methodID及传入方法变量数组执行静态非虚实例方法 |
关于异常的操作
在这里需要注意的是,jni层在出错的时候不会像java层一样,抛出异常,如果需要抛出java层的异常需要调用到如下的方法
序号 |
方法名 |
备注 |
1 |
jint Throw(JNIEnv *env, jthrowable obj); |
obj:一个java.lang.Throwable对象。return:异常抛出结果。0表示异常正常抛出到JVM,否则异常抛出失败。 |
2 |
jint ThrowNew(JNIEnv *env, jclass clazz,const char *message); |
抛出自定义异常clazz:待抛出的异常类。message:异常消息。要求UTF-8编码。return:异常抛出结果。0表示异常正常抛出到JVM,否则异常抛出失败。 |
3 |
jthrowable ExceptionOccurred(JNIEnv *env); |
如果我们需要知道我们之前的操作是否存在JVM抛出的异常时,我们可以调用如下方法获取异常对象,注意:即使我们获取到了异常对象,这个异常仍然在JVM中存在,直到我们调用ExceptionClear方法清空异常。return:当前发生的异常对象。如果没有异常抛出则返回NULL。 |
4 |
void ExceptionDescribe(JNIEnv *env); |
当我们拦截到一个异常,我们可以使用如下方法打印错误栈中的内容。就像Java中的printStackTrace: |
5 |
void ExceptionClear(JNIEnv *env); |
调用以下方法可以清空当前产生的全部异常信息 |
6 |
void FatalError(JNIEnv *env, const char *msg); |
对于上述异常,我们可以产生也可以拦截。但是如果发生了一些错误导致我们的程序无法再正常运行下去了,则可以发送一个错误信息给JVM,此时程序将被终止。msg:错误信息。UTF-8。 |
7 |
jboolean ExceptionCheck(JNIEnv *env); |
我们可以通过以下方法查看当前有没有异常产生。return:是否存在异常信息。 |
关于引用类型
在JNI中引用类型分为三种,分别是全局引用,局部引用和弱全局引用。
全局引用可以跨方法(本地方法返回后仍然有效),跨线程使用,直到手动释放才会失效。该引用不会被GC回收。相当于java中的强引用
局部引用是JVM负责的引用类型,其被JVM分配管理,并占用JVM的资源。局部引用在native方法返回后被自动回收。局部引用只在创建它们的线程中有效,不能跨线程传递。
弱全局引用是一种特殊的全局引用。跟普通的全局引用不同的是,一个弱全局引用允许Java对象被垃圾回收器回收。当垃圾回收器运行的时候,如果一个对象仅被弱全局引用所引用,则这个引用将会被回收。一个被回收了的弱引用指向NULL,开发者可以将其与NULL比较来判定该对象是否可用。
序号 |
方法名 |
备注 |
1 |
jobject NewGlobalRef(JNIEnv *env, jobject obj); |
通过以下方法可以将任意引用转换为全局引用。由于全局引用不再受到JVM统一管理,所以我们要在不用时手动删除。obj:任意类型的引用。return:全局引用。如果内存不足返回NULL。 |
2 |
void DeleteGlobalRef(JNIEnv *env, jobject globalRef); |
通过以下方法可以删除一个全局引用。globalRef:全局引用。 |
3 |
jobject NewLocalRef(JNIEnv *env, jobject ref); |
通过以下方法可以创建一个局部引用。通常情况下,我们在native方法中创建的引用都是局部引用,并且不需要手动进行释放,当方法返回时,这个引用就会被自动销毁。ref:全局或者局部引用return:局部引用 |
4 |
void DeleteLocalRef(JNIEnv *env, jobject localRef); |
通过以下方法可以删除一个局部引用。localRef:局部引用。 |
5 |
jint EnsureLocalCapacity(JNIEnv *env, jint capacity); |
虚拟机将确保每个本地方法至少可以创建16个局部引用。但是在如今的场景中,16个局部引用已经远远不能满足开发需求了。为了为了解决这个问题,JNI提供了查询可用引用容量的方法,我们在创建超出限制的引用时最好先确认是否有足够的空间可以。capacity:给定局部引用的数量。return:JNI_OK表示当前线程栈可以创建capacity个局部引用。返回其他值表示不可用,并抛出一个OutOfMemoryError异常存在异常OutOfMemoryError |
6 |
jint PushLocalFrame(JNIEnv *env, jint capacity); |
局部栈帧的入栈capacity:给定局部引用的数量。return:JNI_OK表示当前线程栈可以创建capacity个局部引用。返回其他值表示不可用,并抛出一个OutOfMemoryError异常 |
7 |
jobject PopLocalFrame(JNIEnv *env, jobject result); |
销毁其中的全部局部引用。result:给定保存栈帧的引用,如果不需要前一个栈帧则可以传入NULL。return:前一个帧的引用。 |
8 |
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj); |
销毁其中的全部局部引用。result:给定保存栈帧的引用,如果不需要前一个栈帧则可以传入NULL。return:前一个帧的引用。 |
9 |
jobject PopLocalFrame(JNIEnv *env, jobject result); |
新建弱引用obj:任意对象。return:返回弱全局引用,如果obj为NULL则返回NULL。 |
10 |
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj); |
删除弱引用obj:弱全局引用。 |
关于字符串的一些操作
序号 |
方法名 |
备注 |
1 |
jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len); |
新建java字符串。unicodeChars:一个指向Unicode编码的字符数组的指针。len:unicodeChars的长度return:Java字符串对象 |
2 |
jsize GetStringLength(JNIEnv *env, jstring string); |
获取jstring字符串的长度string:Java字符串对象return:字符串长度 |
3 |
const jchar * GetStringChars(JNIEnv *env, jstring string,jboolean *isCopy); |
获取jstring字符串的字符串数组。isCopy:注意,这个参数很重要,这是一个指向Java布尔类型的指针。函数返回之后应当检查这个参数的值,如果值为JNI_TRUE表示返回的字符是Java字符串的拷贝,我们可以对其中的值进行任意修改。如果返回值为JNI_FALSE,表示这个字符指针指向原始Java字符串的内存,这时候对字符数组的任何修改都将会原始字符串的内容。如果你不关心字符数组的来源,或者说你的操作不会对字符数组进行任何修改,可以传入NULL。return:指向字节数组的指针 |
4 |
void ReleaseStringChars(JNIEnv *env, jstring string,const jchar *chars); |
释放从Java字符串中获取的字符数组string:Java字符串对象。chars:字符数组。 |
5 |
jstring NewStringUTF(JNIEnv *env, const char *bytes); |
新建UTF-8编码字符串。bytes:UTF-8编码的字节数组。return:UTF-8编码的Java字符串对象 |
6 |
jsize GetStringUTFLength(JNIEnv *env, jstring string); |
获取UTF-8字符串的长度 |
7 |
const char * GetStringUTFChars(JNIEnv *env, jstring string,jboolean *isCopy); |
获取UTF-8编码的Java字符串的 |
8 |
void ReleaseStringUTFChars(JNIEnv *env, jstring string,const char *utf); |
释放从UTF-8字符串中获取的字符数组 |
9 |
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); |
从Java字符串中截取一段字符。str:Java字符串对象。start:起始位置。len:截取长度。buf:保存截取结果的缓冲区。 |
10 |
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf); |
从UTF-8字符串中截取一段字符 |
11 |
const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy); |
直接获取字符串指针 |
12 |
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray); |
直接获取字符串指针 |
关于java数组的一些操作
序号 |
方法名 |
备注 |
1 |
jsize GetArrayLength(JNIEnv *env, jarray array); |
获取数组长度 |
2 |
jobjectArray NewObjectArray(JNIEnv *env, jsize length,jclass elementClass, jobject initialElement); |
新建对象数组ength:数组的长度。elementClass:数组中的对象类型。initialElement:数组中的每个元素都会使用这个值进行初始化,可以为NULL。return:对象数组,创建失败返回NULL |
3 |
jobject GetObjectArrayElement(JNIEnv *env,jobjectArray array, jsize index); |
获取对象数组元素。array:对象数组,index:位置索引 |
4 |
void SetObjectArrayElement(JNIEnv *env, jobjectArray array,jsize index, jobject value); |
设置对象数组元素 |
5 |
j<type>Array New<type>Array(jsize length) |
新建对象数组元素 |
6 |
<type>* Get<type>ArrayElements(jbooleanArray array, jboolean* isCopy) |
获取基本数据类型数组元素的函数原型 |
7 |
void Release<type>ArrayElements(<type>Array array, jboolean* elems,jint mode) |
释放基本数据类型数组。mode 0 :copy back the content and free the elems buffer,mode JNI_COMMIT:copy back the content but do not free the elems buffer,mode JNI_ABORT:free the buffer without copying back the possible changes |
8 |
(Get<type>ArrayRegion)(JNIEnv, <type>Array,jsize, jsize, jboolean*); |
截取数组 |
9 |
void Set<type>ArrayRegion(JNIEnv *env, ArrayType array,jsize start, jsize len, const NativeType *buf); |
设置数组范围 |
10 |
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy); |
操作数组指针 |
11 |
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode); |
操作数组指针 |
本地方法
当我们在一个Java文件中书写一个native的方法的时候,为了让JNI识别我们的方法,就需要采用注册的方式。
序号 |
方法名 |
备注 |
1 |
jint RegisterNatives(JNIEnv *env, jclass clazz,const JNINativeMethod *methods, jint nMethods); |
注册本地方法。clazz: 包含本地方法的Java类。methods: 本地方法描述数组。nMethods: 数组长度。return:成功返回0,否则注册失败 |
2 |
jint UnregisterNatives(JNIEnv *env, jclass clazz); |
return:返回0表示成功,否则为失败。解除本地方法,当我们确定不再需要本地方法的时候,可以调用这个方法来解除本地方法的注册。这个方法会导致本地库的重新加载和链接。 |
网友评论