Android JNI学习(四)——JNI的常用方法的API

前三篇主要讲解了jni基础相关的理论知识,今天主要讲解一下JNI的常用方法的API,掌握了基本的理论知识和常用的API接下来才能更好的实战。

jni的常用API大纲

再看API前,我建议大家主要结合官网的JNI的常用接口文档进行学习。这里我结合官网大致翻译并分类总结了一下,目的主要是帮助我们更好更快的理解JNI中常用的API。如下图:
在这里插入图片描述
下面我列出官网的jni的API文档,方便大家学习。jni官方常用API文档

一、JNI接口函数表(Interface Function Table)

在看函数表之前,我们先看官网的一端介绍。

Each function is accessible at a fixed offset through the JNIEnv argument. The JNIEnv type is a pointer to a structure storing all JNI function pointers. It is defined as follows: The VM initializes the function table, as shown by Code Example 4-1. Note that the first three entries are reserved for future compatibility with COM. In addition, we reserve a number of additional NULL entries near the beginning of the function table, so that, for example, a future class-related JNI operation can be added after FindClass, rather than at the end of the table.Note that the function table can be shared among all JNI interface pointers.

这句话是什么意思了,翻译过来就是如下内容:

每个函数都可以通过JNIEnv参数访问,JNIEnv类型是指向一个存放所有JNI接口指针的指针,其定义如下:typedef const struct JNINativeInterface *JNIEnv; 虚拟机初始化函数表,如下面代码所示,前三个条目是为了将来和COM兼容而保留的。另外,我们在函数表的开头附近保留了一些额外的NULL条目,例如,可以在FindClass之后添加未来与类相关的JNI操作,而不是在表的末尾。请注意,函数表可以在所有JNI接口指针之间共享。

看了官网的解说,我们大致明白了,每个函数都可以通过JNIEnv参数访问,JNIEnv类型是指向一个存放所有JNI接口指针的指针,而所以的API都存在函数表JNINativeInterface中。查看JNINativeInterface大致API如下:

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版本信息(Version Information GetVersion)

获取jni版本信息可以通过GetVersion函数获取:

jint GetVersion(JNIEnv *env);

GetVersion接口主要是返回本机方法接口的版本。如果你仔细查看过jni.h文件,你会发现有如下的一段代码:

/*
 * Manifest constants.
 */
#define JNI_FALSE   0
#define JNI_TRUE    1

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

#define JNI_OK          (0)         /* no error */
#define JNI_ERR         (-1)        /* generic error */
#define JNI_EDETACHED   (-2)        /* thread detached from the VM */
#define JNI_EVERSION    (-3)        /* JNI version error */

#define JNI_COMMIT      1           /* copy content, do not free buffer */
#define JNI_ABORT       2           /* free buffer w/o copying back */

#endif  /* JNI_H_ */

这是什么意思了,原来在不同的JDK环境下返回值是不同的。他们的对应关系如下:

In JDK/JRE 1.1, GetVersion() returns 0x00010001.

In JDK/JRE 1.2, GetVersion() returns 0x00010002.

In JDK/JRE 1.4, GetVersion() returns 0x00010004.

In JDK/JRE 1.6, GetVersion() returns 0x00010006.

仔细看官网,描述的非常清晰,下面是官网文档的一个截图:
在这里插入图片描述

三、Java类的操作

(一),类的加载(DefineClass)

类的加载API如下:

jclass DefineClass(JNIEnv *env, const char *name, jobject loader,const jbyte *buf, jsize bufLen);

这个主要作用是从原始类数据的缓冲区加载类。该buffer包含类调用时未被虚拟机所引用的原始类数据。

参数说明:
官网说明大致如下:

env: the JNI interface pointer.

name: the name of the class or interface to be defined. The string is encoded in modified UTF-8.

loader: a class loader assigned to the defined class.

buf: buffer containing the .class file data.

bufLen: buffer length.

翻译成中文参数大致意思如下:

  1. env:JNI接口指针
  2. name:所定义的类名或者接口名,该字符串有modefied UTF-8编码
  3. loader:指派给定义的类加载器
  4. buf:包含.class文件数据的buffer
  5. bufLen:buffer长度

返回说明:
返回Java类对象,如果发生错误,则返回NULL。可能异常THROWS如下:

  1. ClassFormatError: if the class data does not specify a valid class.
  2. ClassCircularityError: if a class or interface would be its own superclass or superinterface.
  3. OutOfMemoryError: if the system runs out of memory.
  4. SecurityException: if the caller attempts to define a class in the “java” package tree.

翻译成中文就是如下:

  1. 如果类数据没有指定有效的Java类,则会抛出ClassFormatError
  2. 如果是一个类/接口是它自己的一个父类/父接口,则会抛出ClassCircularityError
  3. 如果系统内存不足,则会抛出OutOfMemoryError
  4. 如果调用方试图在“Java”包中定义一个类,则会抛出SecurityException

(二),查找类(FindClass)

jclass FindClass(JNIEnv *env, const char *name);

在JDK1.1版中,此函数加载本地定义的类。它在CLASSPATH环境变量指定的目录和zip文件中搜索具有指定名称的类。

自从Java的SDK发行版1.2以来,Java安全模型允许非系统类加载跟调用本地方法。FindClass定位与当前本机方法关联的类加载器;即声明本机方法的类的类加载器。如果本机方法属于系统类,则不涉及类装入器。否则,将调用适当的类加载器来加载和链接指定的类。从Java 2 SDK1.2版本开始,通过调用接口调用FindClass时,没有当前的本机方法或其关联的类加载器。在这种情况下类加载器.getSystemClassLoader已使用。这是虚拟机为应用程序创建的类加载器,并且能够找到java.class.path属性列出的类。

参数说明:

  1. env:JNI接口指针
  2. name:一个完全限定的类名,即包含“包名”+“/”+类名。举个例子:如java.lang.String,该参数为java/lang/String;如果类名以[开头,将返回一个数组类。比如数组类的签名为java.lang.Object[],该参数应该为"[Ljava/lang/Object"

返回结果说明:
从完全限定名返回类对象,如果找不到该类,则返回NULL。

可能抛出的异常THROWS:

  1. ClassFormatError: if the class data does not specify a valid class.
  2. ClassCircularityError: if a class or interface would be its own superclass or superinterface.
  3. NoClassDefFoundError: if no definition for a requested class or interface can be found.
  4. OutOfMemoryError: if the system runs out of memory.

这些异常中文翻译过来就是:

  1. 如果没有指定这个Java类的,则会抛出ClassFormatError
  2. 如果是一个类/接口是它自己的一个父类/父接口,则会抛出ClassCircularityError
  3. 如果没有找到该类/接口的定义,则抛出NoClassDefFoundError
  4. 如果内存不足,则会抛出OutOfMemoryError

(三)、查找父类(GetSuperclass)

jclass GetSuperclass(JNIEnv *env, jclass clazz);

如果clazz不是Object类,则此函数将返回表示该clazz的父类的Class对象,如果该类是Object,或者clazz代表接口,则此函数返回NULL。

参数说明:

  1. env:JNI接口指针
  2. clazz:Java的Class类

返回说明:

  如果clazz有父类则返回其父类,如果没有其父类则返回NULL

(四)、安全转换

jboolean IsAssignableFrom(JNIEnv *env,jclass clazz1,jclass clazz2);

判断clazz1的对象是否可以安全地转化为clazz2的对象。

参数说明:

  1. env:JNI接口指针
  2. clazz1:Java的Class类,即需要被转化的类
  3. clazz2:Java的Class类,即需要转化为目标的类

返回结果说明:
如果以下任一项为真,则返回JNI_TRUE:

  1. The first and second class arguments refer to the same Java class.
  2. The first class is a subclass of the second class.
  3. The first class has the second class as one of its interfaces.

翻译返回说明:

  1. 如果clazz1和clazz2是同一个Java类。
  2. 如果clazz1是clazz2的子类
  3. 如果clazz1是clazz2接口的实现类

四、异常 操作(Exceptions Throw)

(一).抛出异常:

jint Throw(JNIEnv *env,jthrowable obj);

传入一个jthrowable对象,并且在JNI并将其抛起

引起java.lang.Throwable可丢弃要抛出的对象。

参数说明:

  1. env: the JNI interface pointer.
  2. obj: a java.lang.Throwable object.

参数翻译解说:

  1. env:JNI接口指针
  2. jthrowable:一个Java的java.lang.Throwable对象

返回说明:
成功时返回0;失败时返回负值。

可能抛出的异常:

抛出一个java.lang.Throwable 对象

(二)构造一个新的异常并抛出(ThrowNew)

jint ThrowNew(JNIEnv *env, jclass clazz,const char *message);

传入一个message,并用其构造一个异常并且抛出。

参数说明:

  1. env: the JNI interface pointer.
  2. clazz: a subclass of java.lang.Throwable.
  3. message: the message used to construct the java.lang.Throwable object. The string is encoded in modified UTF-8.

参数翻译解说:

  1. env:JNI接口指针
  2. clazz:一个Java的java.lang.Throwable对象
  3. message: 用于构造java.lang.Throwable可丢弃对象。字符串以修改的UTF-8编码。

返回说明:
成功时返回0;失败时返回负值。

可能抛出的异常:
抛出一个新构造的 java.lang.Throwable 对象

(三)、检查是否发生异常,并抛出异常(ExceptionOccurred)

jthrowable ExceptionOccurred(JNIEnv *env);

检测是否发生了异常,如果发生了,则返回该异常的引用(再调用ExceptionClear()函数前,或者Java处理异常前),如果没有发生异常,则返回NULL。

参数说明:

  1. env: the JNI interface pointer.
    参数翻译解说:
  2. env:JNI接口指针

返回说明:
返回当前正在抛出的异常对象,如果当前没有抛出异常,则返回NULL。

(四)、打印异常的堆栈信息(ExceptionDescribe)

void ExceptionDescribe(JNIEnv *env);

打印这个异常的堆栈信息。这是为调试提供的一个方便的例程。

参数说明:

  1. env: the JNI interface pointer.

参数翻译解说:

  1. env:JNI接口指针

(五)、清除异常的堆栈信息(ExceptionClear)

void ExceptionClear(JNIEnv *env);

清除当前引发的任何异常。如果当前没有抛出异常,则此例程无效。

参数说明:

  1. env: the JNI interface pointer.

参数翻译解说:

  1. env:JNI接口指针

(六)、致命异常(FatalError)

void FatalError(JNIEnv *env, const char *msg);

致命异常,用于输出一个异常信息,并终止当前VM实例,即退出程序。

参数说明:

  1. env: the JNI interface pointer.
  2. an error message. The string is encoded in modified UTF-8.

参数翻译解说:

  1. env:JNI接口指针
  2. msg:异常的错误信息,该字符串用modified UTF-8编码

(七)、检查是否发生异常

jboolean ExceptionCheck(JNIEnv *env);

检查是否已经发生了异常,如果已经发生了异常,则返回JNI_TRUE,否则返回JNI_FALSE。

参数说明:

  1. env: the JNI interface pointer.

参数翻译解说:

  1. env:JNI接口指针

返回说明:

  如果已经发生异常,返回JNI_TRUE,如果没有发生异常则返回JNI_FALSE

五、全局引用和局部引用

(一)、创建全局引用(NewGlobalRef)

jobject NewGlobalRef(JNIEnv *env, jobject obj);

为obj参数引用的对象创建新的全局引用。obj参数可以是全局或局部引用。必须通过调用DeleteGlobalRef()显式处理全局引用。

参数说明:

  1. env: the JNI interface pointer.
  2. obj: a global or local reference.

参数翻译解说:

  1. env:JNI接口指针
  2. object对象

返回说明:

返回全局引用jobject,如果系统内存不足,则返回NULL。

(二)、删除全局引用(DeleteGlobalRef)

void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

删除globalRef指向的全局引用。

参数说明:

  1. env: the JNI interface pointer.
  2. globalRef: a global reference.

参数翻译解说:

  1. env:JNI接口指针
  2. globalRef:需要被删除的全局引用

(三)、删除局部引用(DeleteLocalRef)

本地引用在本机方法调用期间有效。它们在本机方法返回后自动释放。每个本地引用都会消耗一些Java虚拟机资源。程序员需要确保本机方法不会过度分配本地引用。尽管本地引用在本机方法返回Java后会自动释放,但是本地引用的过度分配可能会导致VM在执行本机方法期间耗尽内存。

void DeleteLocalRef(JNIEnv *env, jobject localRef);

参数说明:

  1. env: the JNI interface pointer.
  2. localRef: a local reference.

参数翻译解说:

  1. env:JNI接口指针
  2. localRef:需要被删除的局部引用。

注意:

JDK/jre1.1提供了上面的DeleteLocalRef函数,以便程序员可以手动删除本地引用。例如,如果本机代码遍历一个可能很大的对象数组,并且在每次迭代中使用一个元素,那么在下一次迭代中创建新的本地引用之前,删除对不再使用的数组元素的本地引用是一个很好的做法。
从JDK/jre1.2开始,为本地引用生存期管理提供了一组附加的函数。它们是下面列出的四个功能。

(四)、设定局部变量的容量(EnsureLocalCapacity)

jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

确保在当前线程中至少可以创建给定数量的本地引用。成功时返回0;否则返回负数并抛出OutOfMemoryError。在进入本机方法之前,VM会自动确保至少可以创建16个本地引用。

为了向后兼容性,VM分配超出保证容量的本地引用。(作为调试支持,VM可能会向用户发出警告,即创建的本地引用太多。在JDK中,程序员可以提供-verbose:jni命令行选项来打开这个消息。如果无法创建超出保证容量的本地引用,VM将调用FatalError

参数说明:

  1. env: the JNI interface pointer.
  2. capacity: a capacity number.

参数翻译解说:

  1. env:JNI接口指针
  2. capacity:capacity的数量。

返回说明:

成功返回0,失败返回一个负数,并会抛出一个OutOfMemoryError

(五)、在老的上创建一个新的帧(PushLocalFrame)

jint PushLocalFram(JNIEnv *env ,jint capacity);

创建新的本地参照帧,其中至少可以创建给定数量的局部引用。在成功时返回0,成功返回0,失败返回一个负数并抛出一个OutOfMemoryError异常。

注意,在以前的本地帧中已经创建的局部引用在当前本地帧中仍然有效。
参数说明:

  1. env: the JNI interface pointer.
  2. capacity: a capacity number.

参数翻译解说:

  1. env:JNI接口指针
  2. capacity:给定数量的引用。

(六)、释放一个局部引用(PopLocalFrame)

jobject PopLocalFrame(JNIEnv *env,jobject result)

创建新的本地参照帧,其中至少可以创建给定数量的局部引用。 成功返回0,失败返回一个负数,并会抛出一个OutOfMemoryError

注意,在以前的本地帧中已经创建的局部引用在当前本地帧中仍然有效。
参数翻译解说:

  1. env:JNI接口指针
  2. result:需要释放的局部引用。

(七)、创建一个局部引用(NewLocalRef)

jobject NewLocalRef(JNIEnv *env, jobject ref);

创建一个引用自ref的局部引用。ref可以是全局或者局部引用,如果ref为NULL,则返回NULL。

参数翻译解说:

  1. env:JNI接口指针
  2. ref:局部引用或者全局引用。

(八)、弱全局引用

弱全局引用是一种特殊的全局引用,不像一般的全局引用,一个弱全局引用允许底层Java对象能够被垃圾回收。弱全局引用能够应用在任何全局或局部引用被使用的地方。当垃圾回收器运行的时候,如果对象只被弱引用所引用时,它将释放底层变量。一个弱阮菊引用指向一个被释放的对象相当于等于NULL。编程人员可以通过使用isSampleObject对比弱引用和NULL来检测一个弱全局应用是否指向一个被释放的对象。弱全局引用在JNI中是Java弱引用的一个简化版本,在Java平台API中有有效。

当Native方法正在运行的时候,垃圾回收器可能正在工作,被弱引用所指向的对象可能在任何时候被释放。弱全局引用能够应用在任何全局引用所使用的地方,通常是不太适合那么做的,因为它们可能在不注意的时候编程NULL。

当IsSampleObject能够识别一个弱全局引用是不是指向一个被释放的对象,但是这不妨碍这个对象在被检测之后马上被释放。这就说明了,程序员不能依赖这个方法来识别一个弱全局引用是否能够在后续的JNI函数调用中被使用。

如果想解决上述的问题,建议使用JNI函数NewLocalRef或者NewGlobalRef来用标准的全局也引用或者局部引用来指向相同的对象。如果这个独享已经被释放了这些函数会返回NULL。否则会返回一个强引用(这样就可以保证这个对象不会被释放)。当不需要访问这个对象时,新的引用必须显式被删除。

  1. 创建全局弱引用(NewWeakGlobalRef)
    jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);

创建一个新的弱全局引用。如果obj指向NULL,则返回NULL。如果VM内存溢出,将会抛出异常OutOfMemoryError
参数翻译解说:

  1. env:JNI接口指针
  2. obj:引用对象

返回说明:
全局弱引用

  1. 2、删除全局弱引用(DeleteWeakGlobalRef)
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

VM根据所给定的弱全局引用删除对应的资源。

参数翻译解说:

  1. env:JNI接口指针
  2. obj:将删除的弱全局引用

六、对象操作

(一)、直接创建一个Java对象(AllocObject)

jobject AllocObject(JNIEnv *env, jclass clazz);

分配一个新的Java对象,而不调用该对象的任何构造函数。返回对对象的引用。
注意:clazz参数不能引用数组类。

参数说明:

  1. env: the JNI interface pointer.
  2. clazz: a Java class object.

参数翻译解说:

  1. env:JNI接口指针
  2. clazz::Java类对象

返回说明:

返回一个Java对象,如果该对象无法被创建,则返回NULL。

异常THROWS:

  1. InstantiationException: if the class is an interface or an abstract class.
  2. OutOfMemoryError: if the system runs out of memory.

异常THROWS解说:

  1. 如果该类是接口或者是抽象类,则抛出InstantiationException
  2. 如果是内存溢出,则抛出OutOfMemoryError

(二)根据构造函数创建Java对象(NewObject/NewObjectA/NewObjectV)

jobject NewObject(JNIEnv *env, jclass clazz,jmethodID methodID, ...);

jobject NewObjectA(JNIEnv *env, jclass clazz,jmethodID methodID, const jvalue *args);

jobject NewObjectV(JNIEnv *env, jclass clazz,jmethodID methodID, va_list args);

构造一个新的Java对象,methodID表明需要调用一个构造函数。这个ID必须通过调用GetMethodID()获得,GetMethodID()为函数名,void(V)为返回值。clazz参数不能纸箱一个数组类。

  1. NewObject:需要把所有构造函数的入参,放在参数methodID之后。NewObject()接受这些参数并将它们传递给需要被调用的Java的构造函数
  2. NewObjectA:在methodID后面,放了一个类型为jvalue的参数数组——args,该数组存放着所有需要传递给构造函数的参数。NewObjectA()接收到这个数组中的所有参数,并且按照顺序将它们传递给需要调用的Java方法。
  3. NewObjectV:在methodID后面,放了一个类型为va_list的args,参数存放着所有需要传递给构造函数的参数。NewObjectv()接收到所有的参数,并且按照顺序将它们传递给需要调用的Java方法。

参数说明:

  1. env:JNI接口指针
  2. clazz::Java类
  3. methodID:构造函数的方法ID

附加参数:

  • NewObject的附加参数:arguments是构造函数的参数
  • NewObjectA的附加参数:args是构造函数的参数数组
  • NewObjectV的附加参数:args是构造函数的参数list

返回说明:

返回Java对象,如果无法构造该对象,则返回NULL。

异常THROWS:

  1. 如果传入的类是接口或者抽象类,则抛出InstantiationException
  2. 如果内存溢出,则抛出OutOfMemoryError

注意:所有异常是由构造函数引发的任何异常。

(三)、获取某个对象的“类”(GetObjectClass)

jclass GetObjectClass(JNIEnv *env,object obj);

返回对象的类。

参数说明:

  1. env: the JNI interface pointer.
  2. obj: a Java object (must not be NULL).

参数翻译解说:

  1. env:JNI接口指针
  2. obj:Java对象,不能为NULL

返回说明:

返回Java类对象。

(四)、获取某个对象的“类型”(GetObjectRefType)

jobjectRefType GetObjectRefType(JNIEnv *env,jobject obj);

返回obj参数引用的对象的类型。参数obj可以是局部引用、全局引用或弱全局引用。

参数说明:

  1. env: the JNI interface pointer.
  2. obj:a local, global or weak global reference.

参数翻译解说:

  1. env:JNI接口指针
  2. obj:局部,全局或弱全局引用

返回说明:

如果参数obj是弱全局引用类型,则返回的是JNIWeakGlobalRefType。
如果参数obj是全局引用类型,则返回值将为JNIGlobalRefType。
如果参数obj是本地引用类型,则返回的是JNILocalRefType。
如果obj参数不是有效引用,则此函数的返回值将为JNINVALIDREFTYPE。

对应关系如下:

  1. JNIInvalidRefType=0:代表obj参数不是有效的引用类型
  2. JNILocalRefType=1:代表obj参数是局部变量类型
  3. JNIGlobalRefType=2:代表obj参数是全局变量类型
  4. JNIWeakGlobalRefType=3:代表obj参数是弱全局有效引用

无效的引用就是没有引用的引用。也就是说,obj指针地址不指向内存中的位置,该位置已从Ref创建函数中分配或从JNI函数返回。因此,NULL将是无效的引用,GetObjectRefType(env,NULL)将返回JNINVAlidReftType。另一方面,null引用(指向null的引用)将返回null引用最初创建为的引用类型。
GetObjectRefType不能用于已删除的引用。由于引用通常是作为指向内存数据结构的指针实现的,这些结构可能被VM中的任何引用分配服务重用,一旦删除,则不会指定GetObjectRefType将返回什么值。

无效的引用就是没有引用的引用。也就是说,obj的指针没有指向内存中创建函数时候的地址,或者已经从JNI函数中返回了。所以说NULL就是无效的引用。并且GetObjectRefType(env,NULL)将返回类型是JNIInvalidRefType。但是空引用返回的不是JNIInvalidRefType,而是它被创建时候的引用类型。

注意:不能在引用在删除的时候,调用该函数。

(五)、判断某个对象是否是某个“类”的子类(IsInstanceOf)

jboolean IsInstanceOf(JNIEnv *env, jobject obj,jclass clazz);

测试obj是否是clazz的一个子类。

参数说明:

  1. env: the JNI interface pointer.
  2. obj: a Java object.
  3. clazz: a Java class object.

参数翻译解说:

  1. env:JNI接口指针
  2. obj:一个Java对象
  3. clazz:一个Java的类

返回说明:
如果obj可以转换为clazz,则返回JNI_TRUE;否则,返回JNI_FALSE。空对象可以强制转换为任何类。

(六)、判断两个引用是否指向同一个引用(IsSameObject)

jboolean IsSameObject(JNIEnv *env, jobject ref1,jobject ref2);

测试两个引用是否引用同一个Java对象。

参数说明:

  1. env: the JNI interface pointer.
  2. ref1: a Java object.
  3. ref2: a Java object.

参数翻译解说:

  1. env:JNI接口指针
  2. ref1:一个Java对象
  3. ref2:一个Java对象

返回说明:
如果ref1和ref2引用同一个Java对象,或者两者都为NULL,则返回JNI_TRUE;否则,返回JNI_ FALSE

(七)、返回属性id(GetFieldID)

jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

获取某个类的非静态属性id。通过方法属性名以及·属性的签名(也就是属性的类型),来确定对应的是哪个属性。通过检索这个属性ID,我们就可以调用Get Field和Set Field了,就是我们常用的get和set`方法

参数说明:

  1. env: the JNI interface pointer.
  2. clazz: a Java class object.
  3. name: the field name in a 0-terminated modified UTF-8 string.
    4.sig: the field signature in a 0-terminated modified UTF-8 string.

参数翻译解说:

  1. env:JNI接口指针
  2. clazz:一个Java类对象
  3. name:以"0"结尾的,而且字符类型是"utf-8"的属性名称
  4. sig: 以"0"结尾的,而且字符类型是"utf-8"的属性签名

返回说明:

返回字段ID,如果操作失败,则返回NULL。

异常THROWS:

  1. NoSuchFieldError: if the specified field cannot be found.
  2. ExceptionInInitializerError: if the class initializer fails due to an exception.
  3. OutOfMemoryError: if the system runs out of memory.

异常THROWS解说:

  1. NoSuchFieldError: 如果找不到指定的属性,则抛出NoSuchFieldError
  2. ExceptionInInitializerError:如果类初始化失败,则抛出ExceptionInitializerError
  3. OutOfMemoryError: 如果内存不足了,则抛出OutOfMemoryError

注意:GetFieldID()可能会导致还未初始化的类开始初始化,同时在获取数组的长度不能使用GetFieldID(),而应该使用GetArrayLength()。

(八)、返回属性id系列(GetField)

NativeType Get<type>Field(JNIEnv *env, jobject obj,jfieldID fieldID);

下面看看官网的详细说明:

在这里插入图片描述
我们可以对照图简单归纳一下:返回某个类的非静态属性的值,这是一组函数的简称,具体对应关系如下:

jobject        GetObjectField(JNIEnv *env,jobject obj,jfieldID fielD)   
jboolean     GetBooleanField(JNIEnv *env,jobject obj,jfieldID fielD)
jbyte           GetByteField(JNIEnv *env,jobject obj,jfieldID fielD)
jchar           GetCharField(JNIEnv *env,jobject obj,jfieldID fielD)
jshort          GetShortField(JNIEnv *env,jobject obj,jfieldID fielD)
jint              GetIntField(JNIEnv *env,jobject obj,jfieldID fielD)
jlong           GetLongField(JNIEnv *env,jobject obj,jfieldID fielD)
jfloat           GetFloatField(JNIEnv *env,jobject obj,jfieldID fielD)
jdouble       GetDoubleField(JNIEnv *env,jobject obj,jfieldID fielD)

细心点,你会发现,这几个获取一系列属性id的方法都有三个一样的参数:env,obj,fielD。下面就解说一下这三个参数。

参数解释:

  1. env:JNI接口指针
  2. obj:Java对象,不能为空
  3. fieldID:有效的fieldID

返回说明:

返回对应的属性的值。

(九)、设置属性id系列(SetField)

void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);

设置属性id系列和获得属性id系列是一一对应得,同样有很多的函数,看看官网截图如下:
在这里插入图片描述

设置某个类的的非静态属性的值。其中具体哪个属性通过GetFieldID()来确定哪个属性。这是一组函数的简称,具体我们参照官网介绍可以归纳如下:

void SetObjectField(jobject)

void SetBooleanField(jboolean)

void SetByteField(jbyte)

void SetCharField(jchar)

void SetShortField(jshort)

void SetIntField(jint)

void SetLongField(jlong)

void SetFloatField(jfloat)

void SetDoubleField(jdouble)

参数说明:

  1. env:JNI接口指针
  2. obj:Java对象,不能为空
  3. fieldID:有效的属性ID
  4. value:属性的新值

(十)、获取某个类的某个方法id

jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

返回类或接口的实例(非静态)方法的方法ID。该方法可以在clazz的一个超类中定义并由clazz继承。方法由其名称和签名决定。

参数说明:

  1. env: the JNI interface pointer.
  2. clazz: a Java class object.
  3. name: the method name in a 0-terminated modified UTF-8 string.
  4. sig: the method signature in 0-terminated modified UTF-8 string

参数翻译说明:

1. env: JNI接口指针
2. clazz:Java类对象
3. name:以0结尾的,并且是"utf-8"的字符串的方法名称。
4. sig:以0结尾的,并且是"utf-8"的字符串的方法签名。

返回说明:

返回方法ID,如果找不到指定的方法,则返回NULL。

异常THROWS说明:

  1. NoSuchMethodError: if the specified method cannot be found.
  2. ExceptionInInitializerError: if the class initializer fails due to an exception.
  3. OutOfMemoryError: if the system runs out of memory.

异常THROWS翻译说明:

  1. NoSuchMethodError: 如果找不到指定的方法,则抛出NoSuchMethodError
  2. ExceptionInInitializerError: 如果类初始化失败,则抛出ExceptionInInitializerError
  3. OutOfMemoryError: 如果内存不够,则抛出OutOfMemoryError

注意:GetMethodID()会造成还未初始化的类,进行初始化如果想获取构造函数的ID,请提供init作为方法名称,并将void(V)作为返回类型

(十一)、调用Java实例的某个非静态方法“系列”(NativeType CallMethod/NativeType CallMethodA/NativeType CallMethodV)

NativeType Call<type>Method(JNIEnv *env, jobject obj,jmethodID methodID, ...);

NativeType Call<type>MethodA(JNIEnv *env, jobject obj,jmethodID methodID, const jvalue *args);

NativeType Call<type>MethodV(JNIEnv *env, jobject obj,jmethodID methodID, va_list args);

这一些列都是在native中调用Java对象的某个非静态方法,它们的不同点在于传参不同。是根据方法ID来指定对应的Java对象的某个方法。methodID参数需要调用GetMethodID()获取。

下面看看官网的详细解说:
在这里插入图片描述
在这里插入图片描述

我们对照官网的解说,大致可以简单做出一下归纳。下面我们来看下他们的不同点:

  • CallMethod:需要把方法的入参放在参数methodID后面。CallMethod()其实把这些参数传递给需要调用的Java方法。
  • CallMethodA:在methodID后面,有一个类型为jvalue的args数组,该数组存放所有需要传递给构造函数的参数。CallMethodA()收到这个数组中的参数,是按照顺序将他们传递给对应的Java方法
  • CallMethodV:在methodID后面,有一个类型Wieva_list的参数args,它存放着所有需要传递给构造函数的参数。CallMethodV()接收所有的参数,并且按照顺序将它们传递给需要调用的Java方法。
Call<type>Method Routine Name            Native Type

CallVoidMethod() 
CallVoidMethodA()                        void
CallVoidMethodV()                       

CallObjectMethod()
CallObjectMethodA()                      jobject
CallObjectMethodV()

CallBooleanMethod()
CallBooleanMethodA()                     jboolean
CallBooleanMethodV()

CallByteMethod()
CallByteMethodA()                        jbyte
CallByteMethodV()

CallCharMethod()
CallCharMethodA()                        jchar
CallCharMethodV()

CallShortMethod() 
CallShortMethodA()                       jshort
CallShortMethodV()

CallIntMethod()
CallIntMethodA()                         jint
CallIntMethodV()

CallLongMethod()
CallLongMethodA()
CallLongMethodV()

CallFloatMethod()
CallFloatMethodA()                        jlong
CallFloatMethodV()

CallDoubleMethod()
CallDoubleMethodA()                      jfloat
CallDoubleMethodV()

参数说明:

  1. env:JNI接口指针
  2. obj:对应的Java对象
  3. methodID:某个方法的方法id

返回说明:

返回调用Java方法的结果。

异常THROWS:

执行Java方法期间引发的异常。

注意:
当需要调用某个"private"函数或者构造函数时,这个methodID必须是obj类的方法,不能是它的父类的方法。

(十二)、调用某个类的非抽象方法(CallNonvirtualMethod/MethodA/MethodV)

调用某一个类的抽象方法包含如下一系列方法:
CallNonvirtualMethod Routines
CallNonvirtualMethodA Routines
CallNonvirtualMethodV Routines

具体函数如下:

NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj,jclass clazz, jmethodID methodID, ...);

NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj,jclass clazz, jmethodID methodID, const jvalue *args);

NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj,jclass clazz, jmethodID methodID, va_list args);

这一系列操作就是根据特定的类,和其方法ID来调用Java对象的实例的非静态方法,methodID参数需要调用GetMethodID()获取。

CallNonvirtual<Type>MethodCall<type>Method是不同的,其中CallNonvirtual<Type>Method是基于"类",而和Call<type>Method是基于类的对象。所以说CallNonvirtual<Type>Method的入参是 clazz,methodID必须来源与obi的类,而不是它的父类。

下面我们来看下他们的不同点,归纳如下:

  • CallNonvirtual<type>Method :需要把方法的入参放在参数methodID后面。CallNonvirtual<type>Method()其实把这些参数传递给需要调用的Java方法。
  • CallNonvirtual<type>Method:在methodID后面,有一个类型为jvalue的args数组,该数组存放所有需要传递给构造函数的参数。CallNonvirtual<type>Method()收到这个数组中的参数,是按照顺序将他们传递给对应的Java方法
  • CallNonvirtual<type>MethodV :在methodID后面,有一个类型Wieva_list的参数args,它存放着所有需要传递给构造函数的参数。 CallNonvirtual<type>MethodV()接收所有的参数,并且按照顺序将它们传递给需要调用的Java方法。

对照官网,我们看一下对应关系:
在这里插入图片描述
在这里插入图片描述

我们可以将这一系列函数归纳如下:

CallNonvirtual<type>Method Routine Name         Native Type
CallNonvirtualVoidMethod()
CallNonvirtualVoidMethodA()                       void
CallNonvirtualVoidMethodV()

CallNonvirtualObjectMethod()
CallNonvirtualObjectMethodA()                    jobject
CallNonvirtualObjectMethodV()

CallNonvirtualBooleanMethod()
CallNonvirtualBooleanMethodA()                  jboolean
CallNonvirtualBooleanMethodV()

CallNonvirtualByteMethod()
CallNonvirtualByteMethodA()                     jbyte
CallNonvirtualByteMethodV()

CallNonvirtualCharMethod()
CallNonvirtualCharMethodA()                     jchar
CallNonvirtualCharMethodV()

CallNonvirtualShortMethod()
CallNonvirtualShortMethodA()                    jshort
CallNonvirtualShortMethodV()

CallNonvirtualIntMethod()
CallNonvirtualIntMethodA()                      jint
CallNonvirtualIntMethodV()

CallNonvirtualLongMethod()
CallNonvirtualLongMethodA()                     jlong
CallNonvirtualLongMethodV()

CallNonvirtualFloatMethod()
CallNonvirtualFloatMethodA()                    jfloat
CallNonvirtualFloatMethodV()

CallNonvirtualDoubleMethod()
CallNonvirtualDoubleMethodA()                   jdouble
CallNonvirtualDoubleMethodV()

参数说明:

  1. env:JNI接口指针
  2. obj:Java对象
  3. clazz:Java类
  4. methodID:方法ID

返回说明:

调用Java方法的结果。

异常THROWS:

执行Java方法期间引发的异常。

(十三)、获取静态属性

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

获取某个类的某个静态属性ID,根据属性名以及标签来确定是哪个属性。GetStaticField()SetStaticField()通过使用属性ID来对属性进行操作的。如果这个类还没有初始化,直接调用GetStaticFieldID()会引起这个类进行初始化。

参数说明:

  1. env: the JNI interface pointer.
  2. clazz: a Java class object.
  3. name: the static field name in a 0-terminated modified UTF-8 string.
  4. sig: the field signature in a 0-terminated modified UTF-8 string.

参数翻译说明:

  1. env:JNI接口指针
  2. clazz:Java类
  3. name:静态属性的属性名,是一个编码格式"utf-8"并且以0结尾的字符串。
  4. sig:属性的签名,是一个编码格式"utf-8"并且以0结尾的字符串。

返回说明:

返回字段ID,如果找不到指定的静态字段,则返回NULL。

异常THROWS:

  1. NoSuchFieldError: if the specified static field cannot be found.
  2. ExceptionInInitializerError: if the class initializer fails due to an exception.
  3. OutOfMemoryError: if the system runs out of memory.

异常THROWS解说:

  1. NoSuchFieldError: 如果指定的静态属性无法找到则抛出NoSuchFieldError.
  2. ExceptionInInitializerError: 如果类在初始化失败,则抛出ExceptionInInitializerError.
  3. OutOfMemoryError: 如果内存不够,则抛出OutOfMemoryError.

(十四)、获取静态属性系列(NativeType GetStaticField)

NativeType GetStatic<type>Field(JNIEnv *env,jclass clazz,jfieldID fieldID);

这个系列返回一个对象的静态属性的值。可以通过GetStaticFieldID()来获取静态属性的的ID,有了这个ID,我们就可以获取这个对其进行操作了

下面表明了函数名和函数的返回值,所以只需要替换GetStaticField中的类替换为该字段的Java类型或者表中的实际静态字段存取器。并将NativeType替换为相应的本地类型.

GetStatic<type>Field Routine Name      Native Type
GetStaticObjectField()                 jobject
GetStaticBooleanField()                jboolean
GetStaticByteField()                   jbyte
GetStaticCharField()                   jchar
GetStaticShortField()                  jshort
GetStaticIntField()                    jint
GetStaticLongField()                   jlong
GetStaticFloatField()                  jfloat
GetStaticDoubleField()                 jdouble

参数说明:

  1. env:JNI接口指针
  2. clazz:Java类
  3. field:静态属性ID

返回说明:

返回静态属性

(十五)、设置静态属性系列(SetStaticField)

这个系列是设置类的静态属性的值。可以通过GetStaticFieldID()来获取静态属性的ID。

下面详细介绍了函数名和其值,你可以通过SetStatic并传入的NativeType来设置Java中的静态属性。

SetStatic<type>Field Routine Name         NativeType
SetStaticObjectField()                    jobject
SetStaticBooleanField()                   jboolean
SetStaticByteField()                      jbyte
SetStaticCharField()                      jchar
SetStaticShortField()                     jshort
SetStaticIntField()                       jint
SetStaticLongField()                      jlong
SetStaticFloatField()                     jfloat
SetStaticDoubleField()                    jdouble

参数说明:

  1. env:JNI接口指针
  2. clazz:Java类
  3. field:静态属性ID
  4. value:设置的值

(十六)、获取静态函数ID(GetStaticMethodID)

jmethodID GetStaticMethodID(JNIEnv *env,jclass clazz,const char *name,const char sig);

返回类的静态方法ID,通过它的方法名以及签名来确定哪个方法。如果这个类还没被初始化,调用GetStaticMethodID()将会导致这个类初始化。

参数说明:

  1. env:JNI接口指针
  2. clazz:Java类
  3. name:静态方法的方法名,以"utf-8"编码的,并且以0结尾的字符串
  4. sig:方法签名,以"utf-8"编码的,并且以0结尾的字符串

返回说明:

返回方法ID,如果操作失败,则返回NULL。

异常Exception:

  1. NoSuchMethodError:如果没有找到对应的静态方法,则抛出NoSuchMethodError
  2. ExceptionInInitializerError:如果类初始化失败,则抛出ExceptionInInitializerError
  3. OutOfMemoryError:如果系统内存不足,则抛出OutOfMemoryError

(十七)、调用静态函数系列(NativeType CallStaticMethod/MethodA/MethodV)

NativeType CallStatic<type>Method(JNIEnv *env,jclass clazz,jmethodID methodID,...);
NativeType CallStatic<type>MethodA(JNIEnv *env,jclass clazz,jmethodID methodID,... jvalue *args);
NativeType CallStatic<type>MethodV(JNIEnv *env,jclass,jmethodID methodid, va_list args)

根据指定的方法ID,就可以操作Java对象的静态方法了。可以通过GetStaticMethodID()来获得methodID。方法的ID必须是clazz的,而不是其父类的方法ID。

下面是返回常用的数据类型的函数,我归纳了一下如下:

CallStatic<type>Method Routine Name                Native Type
CallStaticVoidMethod()
CallStaticVoidMethodA()                            void
CallStaticVoidMethodV()

CallStaticObjectMethod()
CallStaticObjectMethodA()                          jobject
CallStaticObjectMethodV()

CallStaticBooleanMethod()
CallStaticBooleanMethodA()                         jboolean
CallStaticBooleanMethodV()

CallStaticByteMethod()
CallStaticByteMethodA()                            jbyte
CallStaticByteMethodV()

CallStaticCharMethod()
CallStaticCharMethodA()                            jchar
CallStaticCharMethodV()

CallStaticShortMethod()
CallStaticShortMethodA()                           jshort
CallStaticShortMethodV()

CallStaticIntMethod()
CallStaticIntMethodA()                             jint
CallStaticIntMethodV()

CallStaticLongMethod()
CallStaticLongMethodA()                            jlong
CallStaticLongMethodV()

CallStaticFloatMethod()
CallStaticFloatMethodA()                           jfloat
CallStaticFloatMethodV()

CallStaticDoubleMethod()
CallStaticDoubleMethodA()                          jdouble
CallStaticDoubleMethodV()

参数说明:

  1. env:JNI接口指针
  2. clazz:Java类
  3. methodID:静态方法ID

返回说明:

返回静态的Java方法的调用方法。

异常Exception:

执行Java方法期间引发的异常。

七、字符串操作

(一)、创建一个字符串(NewString)

jstring NewString(JNIEnv *env,const jchar *unicodeChars,jszie len);

参数说明:

  1. env:JNI接口指针
  2. unicodeChars:指向Unicode字符串的指针
  3. len:unicode字符串的长度

返回说明:

返回一个Java字符串对象,如果该字符串无法被创建在,则返回NULL。

异常THROWS:

如果内存不足,则抛出OutOfMemoryError。

(二)、获取字符串的长度(GetStringLength)

jsize GetStringLength(JNIEnv *env, jstring string);

返回Java字符串的长度(unicode字符的个数)。

参数说明:

  1. env:JNI接口指针
  2. string:Java字符串对象

返回说明:

返回Java字符串的长度。

(三)、获取字符串的指针(GetStringChar)

const jchar* GetStringChar(JNIEnv *env,jstring string , jboolean *isCopy);

返回指向字符串的UNICODE字符数组的指针,该指针一直有效直到被ReleaseStringchars()函数调用。

如果isCopy为非空,则在复制完成后将isCopy设为JNI_TRUE。如果没有复制,则设为JNI_FALSE

参数说明:

  1. env:JNI接口指针
  2. string:Java字符串对象
  3. isCopy:指向布尔值的指针

返回说明:

返回一个指向unicode字符串的指针,如果操作失败,则返回NULL 。

(四)、释放字符串(ReleaseStringChars)

void ReleaseStringChars(JNIEnv *env,jstring string,const jchar *chars);

通过VM,native代码不会再访问chars了。参数chars是一个指针。可以通过GetStringChars()函数获得。

参数说明:

  1. env:JNI接口指针
  2. string:Java字符串对象
  3. chars:指向Unicode字符串的指针

(五)、创建一个UTF-8的字符串(NewStringUTF)

jstring NewStringUTF(JNIEnv *env,const char *bytes);

创建一个UTF-8的字符串。

参数说明:

  1. env:JNI接口指针
  2. bytes:指向UTF-8字符串的指针

返回说明:

Java字符串对象,如果无法构造该字符串,则为NULL。

异常THROWS:
如果系统内存不足,则抛出OutOfMemoryError

(六)、获取一个UTF-8的字符串的长度(GetStringUTFLength)

jsize GetStringUTFLength(JNIEnv *env,jstring string);

以字节为单位,返回字符串UTF-8的长度。

参数说明:

  1. env:JNI接口指针
  2. String:Java字符串对象

返回说明:

返回字符串的UTF-8长度。

(七)、获取StringUTFChars的指针(GetStringUFTChars)

const char *GetStringUFTChars(JNIEnv *env, jString string, jboolean *isCopy);

返回指向UTF-8字符数组的指针,除非该数组被ReleaseStringUTFChars()函数调用释放,否则一直有效。

如果isCopy不是NULL,*isCopy在赋值完成后即被设置为JNI_TRUE。如果未复制,则设置为JNI_FALSE

参数说明:

  1. env:JNI接口指针
  2. String:Java字符串对象
  3. isCopy:指向布尔值的指针

返回说明:

指向UTF-8的字符串指针,如果操作是啊白,则返回NULL。

(八)、释放UTFChars(ReleaseStringUTFChars)

void ReleaseStringUTFChars(JNIEnv *env,jstring string,const char *urf)

通过虚拟机,native代码不再访问了utf了。utf是一个指针,可以调用GetStringUTFChars()获取。

参数说明:

  1. env:JNI接口指针
  2. string:Java字符串对象
  3. utf:指向utf-8字符串的指针

注意:
在JDK/JRE 1.1,程序员可以在用户提供的缓冲区获取基本类型数组元素,从JDK/JRE1.2开始,提供了额外方法,这些方法允许在用户提供的缓冲区获取Unicode字符(UTF-16编码)或者是UTF-8的字符。这些方法如下jdk1.2新的字符串操作方法:

(九)、jdk1.2新的字符串操作方法(GetStringRegion/GetStringUTFRegion)

1),截取一个字符串

void GetStringRegion(JNIEnv *env,jstring str,jsize start,jsize len,jchar *buf)

在str(Unicode字符)从start位置开始截取len长度放置在buf中。如果越界,则抛出StringIndexOutOfBoundsException

2 ),截取一个字符串并将其转换为UTF-8格式

void GetStringUTFRegion(JNIEnv *env,jstring str,jsize start ,jsize len,char *buf);

将str(Unicode字符串)从start位置开始截取len长度并且将其转换为UTF-8编码,然后将结果防止在buf中。

3),截取一个字符串并将其转换为UTF-8格式

const jchar * GetStringCritical(JNIEnv *env,jstring string,jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env,jstring string,cost jchar * carray);

上面这两个函数有点类似于GetStringChars()ReleaseStringChars()功能。如果可能的话虚拟机会返回一个指向字符串元素的指针;否则,则返回一个复制的副本。

注意:

**GetStringChars()ReleaseStringChars()这里两个函数有很大的限制。在使用这两个函数时,这两个函数中间的代码不能调用任何让线程阻塞或者等待JVM的其他线程的本地函数或者JNI函数。有了这些限制,JVM就可以在本地方法持有一个从GetStringCritical得到的字符串的指指针时,禁止GC。当GC被禁止时,任何线程如果出发GC的话,都会被阻塞。而GetStringChars()ReleaseStringChars()**这两个函数中间的任何本地代码都不可以执行会导致阻塞的调用或者为新对象在JVM中分配内存。否则,JVM有可能死活,想象一下这样的场景:

1、只有当前线程触发的GC完成阻塞并释放GC时,由其他线程出发的GC才可能由阻塞中释放出来继续执行。

2、在这个过程中,当前线程会一直阻塞,因为任何阻塞性调用都需要获取一个正在被其他线程持有的锁,而其他线程正等待GC。

GetStringChars()和ReleaseStringChars()的交替迭代调用是安全的,这种情况下,它们的使用必须有严格的顺序限制。而且,我们一定要记住检查是否因为内存溢出而导致它的返回值是NULL。因为JVM在执行GetStringChars()这个函数时,仍有发生数据复制的可能性,尤其是当JVM在内存存储的数组不连续时,为了返回一个指向连续内存空间的指针,JVM必须复制所有数据。

总之,为了避免死锁,在GetStringChars()和ReleaseStringChars()之间不要调用任何JNI函数。`

八、数组操作

(一)、获取数组的长度(GetArrayLength)

jsize GetArrayLength(JNIEnv *env,jarray array)

返回数组中的元素个数,也就是数组的长度。

参数说明:

  1. env:JNI接口指针
  2. array:Java数组

返回说明:

数组的长度。

(二)、创建对象数组(NewObjectArray)

jobjectArray NewObjectArray(JNIEnv *env,jsize length,jclass elementClass, jobject initialElement);

创建一个新的对象数组,它的元素的类型是elementClass,并且所有元素的默认值是initialElement

参数说明:

  1. env:JNI接口指针
  2. length:数组大小
  3. elementClass:数组元素类
  4. initialElement:数组元素的初始值

返回说明:

返回Java数组对象,如果无法构造数组,则返回NULL。

异常THROWS:
如果系统内存不足就抛出OutOfMemoryError异常。

(三)、获取数组元中的某个元素(GetObjectArrayElement)

jobject GetObjectArrayElement(JNIEnv *env,jobjectArray array, jsize index);

返回元素中某个位置的元素。

参数说明:

  1. env:JNI接口指针
  2. array:Java数组
  3. index:数组下标

返回说明:

返回Java对象。

异常THROWS:
如果index下标不是一个有效的下标,则会抛出ArrayIndexOutOfBoundsException

(四)、设置数组中某个元素的值(SetObjectArrayElement)

void SetObjectArrayElement(JNIEnv *env, jobjectArray array,jsize index, jobject value);

设置下标为index元素的值。

参数说明:

  1. env:JNI接口指针
  2. array:Java数组
  3. index:数组下标
  4. value:数组元素的新值

异常THROWS:

如果index不是有效下标,则会抛出ArrayIndexOutOfBoundsException
如果value不是元素类的子类,则会抛出ArrayStoreException

(五)、创建基本类型数组系列(ArrayType NewArray)

ArrayType New<PrimitiveType>Array(JNIEnv *env,jsize length);

用于构造基本类型数组对象的一系列操作。下面说明了特定基本类型数组的创建函数。可以把NewArray替换为某个实际的基本类型数组创建函数 ,然后将ArrayType替换为相应的数组类型。

New<PrimitiveType>Array Routines           Array Type
NewBooleanArray()                          jbooleanArray
NewByteArray()                             jbyteArray
NewCharArray()                             jcharArray
NewShortArray()                            jshortArray
NewIntArray()                              jintArray
NewLongArray()                             jlongArray
NewFloatArray()                            jfloatArray
NewDoubleArray()                           jdoubleArray

参数说明:

  1. env:JNI接口指针
  2. length:数组长度

返回说明:

Java数组,如果无法创建该数组,则返回NULL。

(六)、获取基本类型数组的中数组指针系列(NativeType * GetArrayElements)

NativeType * Get<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array,jboolean * isCopy);

一组返回类型是基本类型的数组指针。在调用相应的Release<PrimitiveType>ArrayElements()函数前将一直有效。由于返回的数组可能是Java数组的副本,因此,对返回数组的变更没有在基本类型中反应出来。除非了调用

一组返回基本类型数组体的函数。结果在调用相应的 Release<PrimitiveType>ArrayElements()函数前将一直有效。由于返回的数组可能是 Java 数组的副本,因此对返回数组的更改不必在基本类型数组中反映出来,直到调用Release<PrimitiveType>ArrayElements()函数。

如果isCopy不是NULL,*isCopy在复制完成后即被设为JNI_TRUE。如果未复制,则设为JNI_FALSE

下面说明了特定的基本类型数组元素的具体函数:

  1. 将GetArrayElements替换为表中某个实际的基本> 类型的函数
  2. 将ArrayType替换为对应的数组类型
  3. 将NativeType替换为本地变量

不管布尔数组在Java虚拟机总如何表示,GetBooleanArrayElements()将始终返回一个jboolean类型的指针,其中每一个字节代表一个元素(开包表示)。内存中将确保所有其他类型的数组为连续的。

Get<PrimitiveType>ArrayElements Routines     Array Type         Native Type
GetBooleanArrayElements()                    jbooleanArray      jboolean
GetByteArrayElements()                       jbyteArray         jbyte
GetCharArrayElements()                       jcharArray         jchar
GetShortArrayElements()                      jshortArray        jshort
GetIntArrayElements()                        jintArray          jint
GetLongArrayElements()                       jlongArray         jlong
GetFloatArrayElements()                      jfloatArray        jfloat
GetDoubleArrayElements()                     jdoubleArray       jdouble

参数说明:

  1. env:JNI接口指针
  2. array:Java数组
  3. isCopy:指向布尔值的指针

返回说明:

isCopy:指向布尔值的指针。

(七)、释放基本类型的数组系列(Get ArrayRegion)

void Get<PrimitiveType> ArrayRegion(JNIEnv *env,ArrayType array,jsize start,jsize len,NativeType *buf);

复制基本类型的数组给buff。

下面说明了特定的基本类型数组元素的具体函数:

  1. 将Get ArrayRegion替换下面中某个实际的基本> 类型的函数
  2. 将ArrayType替换为对应的基本数组类型
  3. 将NativeType替换为本地变量
Get<PrimitiveType>ArrayRegion Routine           Array Type              Native Type
GetBooleanArrayRegion()                         jbooleanArray           jboolean
GetByteArrayRegion()                            jbyteArray              jbyte
GetCharArrayRegion()                            jcharArray              jchar
GetShortArrayRegion()                           jshortArray             jhort
GetIntArrayRegion()                             jintArray               jint
GetLongArrayRegion()                            jlongArray              jlong
GetFloatArrayRegion()                           jfloatArray             jloat
GetDoubleArrayRegion()                          jdoubleArray            jdouble

参数说明:

  1. env:JNI接口指针
  2. array:Java数组
  3. start:开始索引
  4. len:需要复制的长度
  5. buf:目标buffer

异常THROWS:

如果索引无效,则抛出ArrayIndexOutOfBoundsException

(九)、把基本类型数组的数组复制过来系列(Set ArrayRegion)

void Set<PrimitiveType> ArrayRegion(JNIEnv *env,ArrayType array,jsize start,jsize len,const NativeType *buf);

主要是冲缓冲区复制基本类型的数组的函数。

下面说明了特定的基本类型数组元素的具体函数:

  1. 将SetArrayRegion替换下面中某个实际的基本> 类型的函数
  2. 将ArrayType替换为对应的基本数组类型
  3. 将NativeType替换为本地变量
Set<PrimitiveType>ArrayRegion Routine        Array Type            Native Type
SetBooleanArrayRegion()                      jbooleanArray         jboolean
SetByteArrayRegion()                         jbyteArray            jbyte
SetCharArrayRegion()                         jcharArray            jchar
SetShortArrayRegion()                        jshortArray           jshort
SetIntArrayRegion()                          jintArray             jint
SetLongArrayRegion()                         jlongArray            jlong
SetFloatArrayRegion()                        jfloatArray           jfloat 
SetDoubleArrayRegion()                       jdoubleArray          jdouble

参数说明:

  1. env:JNI接口指针
  2. array:Java数组
  3. start:开始索引
  4. len:需要复制的长度
  5. buf:源buffer

异常THROWS:

如果索引无效则会抛出ArrayIndexOutOfBoundsException

(十)、补充知识点

从JDK/JER 1.1开始提供**Get/Release<primitivetype>ArrayElements**函数获取指向原始数组元素的指针。如果VM支持锁定,则返回指向原始数组的指针,否则,复制。从JDK/JRE 1.3 开始引入新的功能即便VM不支持锁定,本地代码也可以获取数组元素的直接指针,

void *GetPrimitiveArrayCritical(JNIEnv *env,jarray array,jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env,jarray array,void *carray,jint mode);

虽然这两个函数与上面的Get/Release <primitivetype> ArrayElements函数很像,但是在使用这个功能的时候,还是有很多的限制。

在调用GetPrimitiveArrayCritical之后,调用ReleasePrimitiveArrayCritical之前,这个区域是不能调用其他JNI函数,而且也不能调用任何可能导致线程阻塞病等待另一个Java线程的系统调用。

比如,当前线程不能调用read函数来读取,正在被其他所写入的stream。

九 系统级别的操作

(一) 注册方法(RegisterNatives)

jint RegisterNatives(JNIEnv *env, jclass clazz,const JNINativeMethod *methods, jint nMethods);

根据clazz参数注册本地方法,methods参数制定JNINativeMethod结构数组,该数组包含本地方法的名字、签名及函数指针。其中名字及签名是指向编码为“UTF-8”的指针;nMethod参数表明数组中本地方法的个数。

在介绍参数前,先简单说一下JNINativeMethod 这个结构体,如果你对JNINativeMethod 不了解,可以看我上一篇Android JNI(三)——JNI数据结构之JNINativeMethod。比较系统和详细的解说了JNINativeMethod 结构体,这里不在详细的解说,大致解说一下。

这里简单看一下JNINativeMethod这个结构体构成,以及说下JNINativeMethod这个结构体的参数。

typedef struct {
    
     
char *name; 
char *signature; 
void *fnPtr; 
} JNINativeMethod; 

结构体JNINativeMethod参数说明:

  1. name
    变量name是Java中函数的名字

  2. signature
    变量signature,用字符串是描述了Java中函数的参数和返回值,这个参数最为复杂,接下来我会详细意义介绍。

  3. fnPtr
    变量fnPtr是函数指针,指向native函数。前面都要接 (void *)

注意:

第一个变量name与第三个变量fnPtr是对应的,一个是java层方法名,对应着第三个参数的native方法名字。

RegisterNatives函数参数说明:

  1. env:JNI接口指针
  2. clazz:Java类对象
  3. methods:类中的native方法
  4. nMethod:类中本地方法的个数

返回说明:

成功时返回“0”;失败时返回负值。

异常THROWS:
如果没有找到指定的方法或者方法不是本地方法,则抛出NoSuchMethodError

(二) 注销方法(UnregisterNatives)

jint UnregisterNatives(JNIEnv *env,jclass clazz);

注销本地方法。类回收之前还没有被函数注册的状态。该函数一般不能再Native代码中被调用,它为特定的程序提供了一种重加载重链接本地库的方法。

参数说明:

  1. JNI:接口指针
  2. clazz:Java类对象

返回说明:

注销成功返回0,失败返回负数。

(三) 监视操作(MonitorEnter)

jint MonitorEnter(JNIEnv *env,jobject obj);

obj引用的底层Java对象关联的监视器。obj引用不能为空。每个Java对象都有一个相关的监视器。如果当前线程已经有关联到obj的监视器,它将添加监视器的计数器来表示这个线程进入监视器的次数。如果关联至obj的监视器不属于任何线程,那当前线程将变成该监视器的拥有者,并设置计数器为1,如果其他计数器已经拥有了这个监视器,当前线程将进行等待直到监视器被释放,然后再获得监视器的拥有权。

通过MonitorEnter JNI函数调用的监视器不能用monitorexitJava虚拟机指令或者同步方法退出。MonitorEnterJNI函数调用和monitorenter Java虚拟机指令可能用同样的对象竞争地进入监视器。

为了避免死锁,通过MoniterEnterJNI函数调用进入的监视器必须用MonitorExitJNI调用退出,除非DetachCurrentThread接口被隐式的调用来释放JNI监视器。

参数说明:

  1. JNI:接口指针
  2. obj:普通的Java对象或类对象

返回说明:

注销成功返回0,失败返回负数。

(四) 监视器退出(MonitorExit)

jint MonitorExit(JNIEnv *env,jobject obj);

当前线程拥有与该obj关联的监视器,线程减少计数器的值来指示线程进入监视器的次数。如果计数器的值变为0,则线程释放该监视器。Native代码不能直接调用MonitorExit来释放监视器。而是应该通过同步方法来使用Java虚拟机指令来释放监视器。

参数说明:

  1. JNI:接口指针。
  2. obj:普通的Java对象或类对象。

返回说明:

注销成功返回0,失败返回负数。

异常THROWS:
如果当前线程不拥有该监视器,则应该抛出IllegalMonitorStateException

十 NIO操作

NIO相关操作允许Native代码直接访问java.nio的直接缓冲区。直接缓冲区的内容可能存在于普通的垃圾回收器以外的本地内存。有关直接缓冲区的信息,可以参考 NIO和java.nio.ByteBuffer类的规范。

在JDK/JRE 1.4中引入了新的JNI函数,允许检查和操作做直接缓冲区:

  1. NewDirectByteBuffer
  2. GetDirectBufferAddress
  3. GetDirectBufferCapacity

每个Java虚拟机的实现都必须支持这些功能,但并不是每个实现都需要支持对直接缓冲区的JNI访问。如果JVM不支持这种访问,那么NewDirectByteBufferGetDirectBufferAddress函数必须始终返回NULL,并且GetDirectBufferCapacity函数必须始终返回-1。如果JVM确实支持这种访问,那么必须实现这三个函数才能返回合适的值。

(一) 返回ByteBuffer(NewDirectByteBuffer)

jobject NewDirectByteBuffer(JNIEnv *env,void *address,jlong capacity);

分配并返回直接java.nio.ByteBuffer文件指从内存地址开始并扩展容量字节的内存块。调用此函数并将生成的byte buffer对象返回给Java级代码的本机代码应确保缓冲区引用可用于读写(如果合适)的有效内存区域。从Java代码访问无效内存位置的尝试将返回任意值、没有可见效果,或者引发未指定的异常。

参数说明:

  1. env:JNIEnv接口指针
  2. address:内存区域的起始地址
  3. capacity:内存区域的大小

返回说明:

返回一个新开辟的java.nio.ByteBuffer对象的本地引用。如果产生异常,则返回NULL。如果JVM不支持JNI访问直接缓冲区,也会返回NULL。

异常THROWS:
如果缓冲区分配失败,则返回OutOfMemoryError

(二) 返回直接缓冲区中对象的初始地址(GetDirectBufferAddress)

void* GetDirectBufferAddress(JNIEnv *env,jobject buf);

获取并返回java.nio.Buffer的内存初始地址。该函数允许Native代码通过直接缓冲区对象访问Java代码的同一内存区域。

参数说明:

  1. env:JNIEnv接口指针
  2. buf:java.nio.Buffer对象

返回说明:
返回内存区域的初始地址。如果内存区域未定义,返回NULL,如果给定的对象不是java.nio.buffer,则返回NULL,如果虚拟机不支持JNI访问,则返回NULL。

(三) 返回直接缓冲区中对象的内存容量(GetDirectBufferCapacity)

jlong GetDirectBufferCapacity(JNIEnv *env,jobject buf);

获取并返回java.nio.Buffer的内存容量。该容量是内存区域可容纳的元素的个数。

参数说明:

  1. env:JNIEnv接口指针
  2. buf:java.nio.Buffer对象

返回说明:

返回内存区域的容量。如果指定的对象不是`java.nio.buffer`,则返回-1,或者如果对象是未对齐的view buffer且处理器架构不支持对齐访问。如果虚拟机不支持JNI访问则返回-1。

十一、反射支持

如果程序员知道方法和属性的名称和类型,则直接使用JNI调用Java方法或者访问Java字段。Java核心反射API允许在运行时反射Java类。JNI提供了JNI中使用的字段和方法ID与Java Core Reflection API中使用的字段和方法对象之间的一组转换函数。

(一)、转化获取方法ID(FromReflectedMethod)

jmethodID FromReflectedMethod(JNIEnv *env,jobject method);

java.lang.reflect.Method或者java.lang.reflect.Constructor对象转换为方法ID。

参数说明:

  1. env:JNIEnv接口指针
  2. method:java.lang.reflect.Method或者java.lang.reflect.Constructor对象

返回说明:

返回方法ID.

(二)、转化获取属性ID(FromReflectedField)

jfield FromReflectedField(JNIEnv *env,jobject field);

java.lang.reflect.Field转化属性ID。

参数说明:

  1. env:JNIEnv接口指针
  2. field:java.lang.reflect.Field对象

返回说明:

返回属性ID.

(三)、反转化并获取方法对象(ToReflectedMethod)

jobject ToReflectedMethod(JNIEnv *env,jclass clazz,jmethodID methodID, jboolean isStatic);

将源自cls的方法ID转化为java.lang.reflect.Method或者java.lang.reflect.Constructor对象。如果方法ID指向一个静态属性,isStatic必须设置为JNI_TRUE,否则为JNI_FALSE

参数说明:

  1. env:JNIEnv接口指针
  2. clazz:Java类对象
  3. methodID:Java类对应的方法id
  4. isStatic:是否是静态方法

返回说明:

对应Java层 java.lang.reflect.Method或者java.lang.reflect.Constructor对象。如果失败,则返回0.

异常Throws :
如果内存不足,则抛出OutOfMemoryError

(四)、反转化并获取属性对象(ToReflectedField)

jobject ToReflectedField(JNIEnv *env,jclass cls,jfieldID field,jboolean isStatic)

将来源于cls的属性ID转化为java.lang.reflect.Field对象。如果属性ID指向一个静态属性,isStatic必须设置为JNI_TRUE,否则为JNI_FALSE

参数说明:

  1. env:JNIEnv接口指针
  2. cls:Java类对象
  3. methodID:Java对应的属性ID
  4. isStatic:是否是静态属性

返回说明:

成功返回java.lang.reflect.Field对象,失败返回0.

异常Throws :
如果内存不足,则抛出OutOfMemoryError

十二、获取虚拟机(GetJavaVM)

jint GetJavaVM(JNIEnv *env,JavaVM **vm);

返回当前线程对应的java虚拟机接口。返回的结果保存在vm。

参数说明:

  1. env:JNI接口指针
  2. vm:保存虚拟机指针

返回说明:

成功返回0,失败返回负数.

十三、总结

好了,到了这里关于jni常用函数表便已经归纳讲解完毕,大家也可以参考jni的官网更加系统学习jni相关的知识,这里我列出jni官网api地址,希望对想学习jni的你有所帮助。jni常用接口大全。在接下来的博客里我将讲述Java与Native如何相互调用,在讲述完Java与Native如何相互调用后,将会实战演戏一个demo。更多jni相关知识可以参考我的其余三篇博客:

  1. Android JNI(一)——NDK与JNI基础
  2. Android JNI(二)——实战JNI入门之Hello World。
  3. Android JNI(三)——JNI数据结构之JNINativeMethod。

猜你喜欢

转载自blog.csdn.net/ljx1400052550/article/details/115001243