初述NDK-JNI那些事儿

初述NDK-JNI那些事儿

此章节介绍比较基础,主要介绍一些基础的概念知识,文章属于想到哪写到哪,刚接触JNI的时候,感觉很高大上,心里觉得肯定很难搞定吧,后来慢慢的发现上手其实并不难,难得是精通,是的,所有的事情都难得是精通二字,希望通过记下这些小知识能有大进步吧~

主要介绍ndk-jni的一些基础知识:

  • JNI常用类型
  • Native Method命名
  • Native字段和方法ID
  • Native 日志打印
  • JNI 函数注册方式
  • 常用JNI方法详解

源码的魅力

半吊子的我,总是搞不清楚重点,总是会不经意间就开始浑浑噩噩,深度的思考总是那么可望而不可即,可能是习惯,可能是依赖,遇到问题总是习惯了去找度娘,深入理解是一个很漫长的过程,坚持总是不容易的,又扯远了,拉回正题。了解JNI中的一些基本数据结构,一些数据类型,推荐多看看 Java Native Interface Specification 或者ndk包中的源文件,多看源码,多了解原理,多深入,真的会有想象不到的收获,一个小白的小小建议

JNI常用类型

此处只列举了很小的一部分,详细内容见jni.h头文件

C类型 Native类型 Java类型 对应列表:

c类型 native类型 java类型 签名类型
unsigned char jboolean boolean Z
signed char jbyte byte B
unsigned short jchar char C
short jshort short S
int jint int I
long long jlong long J
float jfloat float F
double jdouble double D
void* jobject Object L

对于数组的签名类型:
int[]-> [I,
int[][]-> [[I,
String[]-> [Ljava/lang/String;

1、基本类型(如整型,字符等)在Java和native之间是采用值传递
2、Java对象采用的是引用传递
3、虚拟机必须保持已传递给native的对象的引用,以使这些对象不被垃圾回收器回收。native code也必须有一种方法通知虚拟机它不再需要某个对象,并且垃圾收集器必须能够将其回收
因此在JNI层也需要特别主要内存回收问题

Native Method命名

package cn.com.analysis;
public class Analysis {
    static {
        System.loadLibrary("analysis");
    }
    public  native  String  showTest();
}
/*
 1. Class:     cn_com_analysis_Analysis
 2. Method:    showTest
 3. Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_cn_com_analysis_Analysis_showTest
  (JNIEnv *, jclass);
JNI层的native方法名有以下几个组成部分:
  1. 前缀 Java_
  2. 完整类名(类名中的.用_代替)
  3. 下划线 _
  4. 方法名(方法名中的特殊字符需要转义)
  5. 参数签名(非必须,有重载方法的时候才需要),如果有重载的本地方法,需要再添加两个下划线__,然后再添加方法签名(由java字段描述符描述,用_代替描述符中的包名分割/符,签名中的特殊字符需要转义)
JNI层的native方法的方法参数有以下组成部分:
  1. JNI接口指针
    native方法的第一个参数,JNI接口指针的类型是JNIEnv
  2. jclass/jobject
    第二个参数取决于native method是否静态方法,如果是非静态方法,那么第二个参数是对对象的引用,如果是静态方法,则第二个参数是对它的class类的引用
  3. 剩下的参数跟Java方法参数一一对应

注意:c语言使用JNI 接口指针的时候的形式是(*env),而c++直接使用env。

Native字段和方法ID

可以使用宏定义的方式将查找Java类,类中方法以及字段等简写为一个函数,关于字段和方法的具体相关函数详见 常用JNI方法详解

#define CG_CACHE_CLASS(v, n) { jclass cc = env->FindClass(n); if (!cc) { return false; } v = static_cast(env->NewGlobalRef(cc)); }
#define CG_CACHE_METHOD(c, m, n, s) { m = env->GetMethodID(c, n, s); if (!m) { return false; } }
#define CG_CACHE_STATIC_METHOD(c, m, n, s) { m = env->GetStaticMethodID(c, n, s); if (!m) { return false; } }
#define CG_CACHE_FIELD(c, f, n, s) { f = env->GetFieldID(c, n, s); if (!f) { return false; } }

Native 日志打印

JNI层打印日志并输出到Java端,可以使用__android_log_print分日志等级进行打印

#include 
static const char *LOGTAG = "jniLog";
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOGTAG, fmt, ##args)
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOGTAG, fmt, ##args)
#define LOGW(fmt, args...) __android_log_print(ANDROID_LOG_WARN, LOGTAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOGTAG, fmt, ##args)

JNI 函数注册方式

Java端主要是通过System.loadLibrary方法对native库进行加载,加载的时候会调用JNI_OnLoad方法,如果系统不支持动态链接,那么所有本地方法需要预链接到虚拟机,也可以调用JNI函数RegisterNatives()来注册该类关联的本地方法,因此就有了静态和动态两种注册方法

静态注册

优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
缺点:

  • 后期类名、文件名改动,头文件所有函数将失效,需要手动改,超级麻烦易出错
  • 代码编写不方便,由于JNI层函数的名字必须遵循特定的格式,且名字特别长;
  • 工作量很大,因为必须为所有声明了native函数的java类编写JNI头文件;
  • 程序运行效率低,因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时。
动态注册

优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

这两种注册方法后续会写文章重点介绍,待续…

常用JNI方法详解

这里只列举了自己在实际开发中用的比较多的方法,更多的方法详解可以查看Java Native Interface Specification 或者JNI教程与技术手册

注册JNI函数,主要是动态注册的时候会用到

 /**
  * 返回本地方法接口的版本
  * @param env JNI接口指针
  * @return 高16位返回主版本号,低16位返回次版本号,如在JDK/JRE 1.6中,返回0x00010006。也有可能返回 JNI_EDETACHED 和 JNI_EVERSION 错误码
  */
 jint (*GetVersion)(JNIEnv *);
 
 /**
  * 为clazz类注册本地方法
  * @param env JNI接口指针
  * @param clazz class对象
  * @param methods clazz类中的本地方法,指向方法数组
  * @param nMethods 本地方法个数
  * @return 0:成功, 负数:失败
  *
  * @throws NoSuchMethodError
  */
 jint (*RegisterNatives)(JNIEnv *, jclass, const JNINativeMethod *, jint);

 /**
  * 获取当前线程关联的Java VM接口
  *
  * @param env JNI接口指针
  * @param vm java VM接口指针
  *
  * @return 0:成功, 负数:失败
  */
 jint (*GetJavaVM)(JNIEnv *, JavaVM **);

对Java类和对象的操作

 /**
  * 用于加载本地定义的类
  * @param env JNI接口指针
  * @param name 完整的包名("/"代替".") 或 数组类型字段描述("["开头,紧跟签名描述),如"java/lang/String" for java.lang.String, "[Ljava/lang/Object;" for java.lang.Object[]
  * @return class对象或NULL
  *
  * @throws ClassFormatError 不是有效的class数据
  * @throws ClassCircularityError 类或接口是自身的父类或自身继承了该接口
  * @throws OutOfMemoryError 内存不足
  * @throws NoClassDefFoundError 找不到name对应的class类
  */
 jclass (*FindClass)(JNIEnv *, const char *);
 
  /**
  * 构造一个新的java对象,method ID指用以生成该类的构造方法,method ID必须是通过GetMethodID()获得
  * @param env JNI接口指针
  * @param clazz 非数组class对象
  * @param ... 传递给构造方法的参数
  * @return java对象 or NULL(对象构造失败)
  *
  * @throws InstantiationException clazz是一个接口或抽象类
  * @throws OutOfMemoryError 内存不足
  */
 jobject (*NewObject)(JNIEnv *, jclass, jmethodID, ...);

 /**
  * 返回对象对应的class对象
  * @param env JNI接口指针
  * @param obj 非空java对象
  * @return class对象
  */
 jclass (*GetObjectClass)(JNIEnv *, jobject);

全局、局部引用的处理

 /**
  * 为传入的obj创建全局引用,obj可以是全局引用也可以是局部引用。全局引用需要调用DeleteGlobalRef来释放
  *
  * @param env JNI接口指针
  * @param obj 全局或局部引用
  *
  * @return 全局引用 or NULL(内存不足)
  */
 jobject (*NewGlobalRef)(JNIEnv *, jobject);
 
 /**
  * 释放全局引用
  *
  * @param env JNI接口指针
  * @param globalRef 全局引用
  */
 void (*DeleteGlobalRef)(JNIEnv *, jobject);

 /**
  * 释放局部引用
  *
  * @param env JNI接口指针
  * @param localRef 局部引用
  */
 void (*DeleteLocalRef)(JNIEnv *, jobject);

 /**
  * 为传入的ref创建局部引用,ref可以是全局引用也可以是局部引用
  *
  * @param env JNI接口指针
  * @param ref 全局或局部引用
  *
  * @return 局部引用 or NULL
  */
 jobject (*NewLocalRef)(JNIEnv *, jobject);

获取非静态方法的Method ID
以及根据 Method ID调用java实例方法的接口

  /**
   * 返回非静态方法的method ID
   * @param env JNI接口指针
   * @param clazz class对象
   * @param name 方法名
   * @param sig 方法签名
   * @return 方法ID or NULL
   *
   * @throws NoSuchMethodError 找不到对应的方法
   * @throws ExceptionInInitializerError class初始化失败
   * @throws OutOfMemoryError 内存不足
   */
  jmethodID (*GetMethodID)(JNIEnv *, jclass, const char *, const char *);

  /**
   * CallMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);调用参数放到可变参数中
   * CallMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);调用参数放入jvalue数组
   * CallMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);调用参数放入va_list结构中
   * 以上三组调用接口都是根据 method ID调用java实例方法(非静态方法)的接口,其中method ID是通过GetMethodID()获取的
   * 当这些方法用于调用java对象的私有方法或构造函数时,method ID必须从obj的真实类获取,而不应从其某个父类获取
   * 是方法的返回类型,三类接口间唯一的区别是methodID参数之后调用参数的不同
   *
   * @param env JNI接口指针
   * @param obj java对象
   * @param methodID 方法ID
   * @param args 调用参数
   * @return java方法返回结果
   *
   * @throws java方法中可能抛出的异常
   */
  jobject (*CallObjectMethod)(JNIEnv *, jobject, jmethodID, ...);
  jboolean (*CallBooleanMethod)(JNIEnv *, jobject, jmethodID, ...);
  jbyte (*CallByteMethod)(JNIEnv *, jobject, jmethodID, ...);
  jchar (*CallCharMethod)(JNIEnv *, jobject, jmethodID, ...);
  jshort (*CallShortMethod)(JNIEnv *, jobject, jmethodID, ...);
  jint (*CallIntMethod)(JNIEnv *, jobject, jmethodID, ...);
  jlong (*CallLongMethod)(JNIEnv *, jobject, jmethodID, ...);
  jfloat (*CallFloatMethod)(JNIEnv *, jobject, jmethodID, ...);
  jdouble (*CallDoubleMethod)(JNIEnv *, jobject, jmethodID, ...);
  void (*CallVoidMethod)(JNIEnv *, jobject, jmethodID, ...);

获取和设置非静态成员变量的field ID

    /**
   * 根据class对象获取非静态成员变量的field ID
   * @param env JNI接口指针
   * @param clazz class对象
   * @param name 变量名
   * @param sig 变量签名
   * @return field ID or NULL
   *
   * @throws NoSuchFieldError 找不到对应的变量ID
   * @throws ExceptionInInitializerError class初始化失败
   * @throws OutOfMemoryError 内存不足
   */
  jfieldID (*GetFieldID)(JNIEnv *, jclass, const char *, const char *);
  
  /**
   * 根据field id取出对象中相应的变量值,field Id通过GetFieldID()获取
   * @param env JNI接口指针
   * @param obj java对象
   * @param fieldID 有效的field id
   * @return 相应的变量值
   */
  jobject (*GetObjectField)(JNIEnv *, jobject, jfieldID);
  jboolean (*GetBooleanField)(JNIEnv *, jobject, jfieldID);
  jbyte (*GetByteField)(JNIEnv *, jobject, jfieldID);
  jchar (*GetCharField)(JNIEnv *, jobject, jfieldID);
  jshort (*GetShortField)(JNIEnv *, jobject, jfieldID);
  jint (*GetIntField)(JNIEnv *, jobject, jfieldID);
  jlong (*GetLongField)(JNIEnv *, jobject, jfieldID);
  jfloat (*GetFloatField)(JNIEnv *, jobject, jfieldID);
  jdouble (*GetDoubleField)(JNIEnv *, jobject, jfieldID);
  
  /**
   * 根据field id为相应的变量设置新的值,field Id通过GetFieldID()获取
   * @param env JNI接口指针
   * @param obj java对象
   * @param fieldID 有效的field id
   * @param value 要设置的值
   */
  void (*SetObjectField)(JNIEnv *, jobject, jfieldID, jobject);
  void (*SetBooleanField)(JNIEnv *, jobject, jfieldID, jboolean);
  void (*SetByteField)(JNIEnv *, jobject, jfieldID, jbyte);
  void (*SetCharField)(JNIEnv *, jobject, jfieldID, jchar);
  void (*SetShortField)(JNIEnv *, jobject, jfieldID, jshort);
  void (*SetIntField)(JNIEnv *, jobject, jfieldID, jint);
  void (*SetLongField)(JNIEnv *, jobject, jfieldID, jlong);
  void (*SetFloatField)(JNIEnv *, jobject, jfieldID, jfloat);
  void (*SetDoubleField)(JNIEnv *, jobject, jfieldID, jdouble);

获取静态方法的method ID
以及根据 method ID调用java实例方法的接口

 /**
  * 返回静态方法的method ID
  * @param env JNI接口指针
  * @param clazz class对象
  * @param name 方法名
  * @param sig 方法签名
  * @return 方法ID or NULL
  *
  * @throws NoSuchMethodError 找不到对应的方法
  * @throws ExceptionInInitializerError class初始化失败
  * @throws OutOfMemoryError 内存不足
  */
 jmethodID (*GetStaticMethodID)(JNIEnv *, jclass, const char *, const char *);

 /**
  * CallStaticMethod(JNIEnv *env, jclass clazz, jmethodID methodID, ...);调用参数放到可变参数中
  * CallStaticMethodA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);调用参数放入jvalue数组
  * CallStaticMethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);调用参数放入va_list结构中
  * 以上三组调用接口都是根据 method ID调用java静态方法的接口,其中method ID是通过GetStaticMethodID()获取的
  * method ID必须从clazz的真实类获取,而不应从其某个父类获取
  * 是方法的返回类型,三类接口间唯一的区别是methodID参数之后调用参数的不同
  * @param env JNI接口指针
  * @param clazz class对象
  * @param methodID 方法ID
  * @param args 调用参数
  * @return java方法返回结果
  *
  * @throws java方法中可能抛出的异常
  */
 jobject (*CallStaticObjectMethod)(JNIEnv *, jclass, jmethodID, ...);
 jboolean (*CallStaticBooleanMethod)(JNIEnv *, jclass, jmethodID, ...);
 jbyte (*CallStaticByteMethod)(JNIEnv *, jclass, jmethodID, ...);
 jchar (*CallStaticCharMethod)(JNIEnv *, jclass, jmethodID, ...);
 jshort (*CallStaticShortMethod)(JNIEnv *, jclass, jmethodID, ...);
 jint (*CallStaticIntMethod)(JNIEnv *, jclass, jmethodID, ...);
 jlong (*CallStaticLongMethod)(JNIEnv *, jclass, jmethodID, ...);
 jfloat (*CallStaticFloatMethod)(JNIEnv *, jclass, jmethodID, ...);
 jdouble (*CallStaticDoubleMethod)(JNIEnv *, jclass, jmethodID, ...);
 void (*CallStaticVoidMethod)(JNIEnv *, jclass, jmethodID, ...);

获取和设置静态成员变量的field ID

  /**
   * 根据class对象获取静态成员变量的field ID
   * @param env JNI接口指针
   * @param clazz class对象
   * @param name 变量名
   * @param sig 变量签名
   * @return field ID or NULL
   *
   * @throws NoSuchFieldError 找不到对应的变量ID
   * @throws ExceptionInInitializerError class初始化失败
   * @throws OutOfMemoryError 内存不足
   */
  jfieldID (*GetStaticFieldID)(JNIEnv *, jclass, const char *, const char *);
  
  /**
   * 根据field id取出对象中相应的变量值,field Id通过GetStaticFieldID()获取
   * @param env JNI接口指针
   * @param clazz class对象
   * @param fieldID 有效的field id
   * @return 相应的静态变量值
   */
  jobject (*GetStaticObjectField)(JNIEnv *, jclass, jfieldID);
  jboolean (*GetStaticBooleanField)(JNIEnv *, jclass, jfieldID);
  jbyte (*GetStaticByteField)(JNIEnv *, jclass, jfieldID);
  jchar (*GetStaticCharField)(JNIEnv *, jclass, jfieldID);
  jshort (*GetStaticShortField)(JNIEnv *, jclass, jfieldID);
  jint (*GetStaticIntField)(JNIEnv *, jclass, jfieldID);
  jlong (*GetStaticLongField)(JNIEnv *, jclass, jfieldID);
  jfloat (*GetStaticFloatField)(JNIEnv *, jclass, jfieldID);
  jdouble (*GetStaticDoubleField)(JNIEnv *, jclass, jfieldID);
  
  /**
   * 根据field id为相应的静态变量设置新的值,field Id通过GetStaticFieldID()获取
   * @param env JNI接口指针
   * @param clazz class对象
   * @param fieldID 有效的field id
   * @param value 要设置的值
   */
  void (*SetStaticObjectField)(JNIEnv *, jclass, jfieldID, jobject);
  void (*SetStaticBooleanField)(JNIEnv *, jclass, jfieldID, jboolean);
  void (*SetStaticByteField)(JNIEnv *, jclass, jfieldID, jbyte);
  void (*SetStaticCharField)(JNIEnv *, jclass, jfieldID, jchar);
  void (*SetStaticShortField)(JNIEnv *, jclass, jfieldID, jshort);
  void (*SetStaticIntField)(JNIEnv *, jclass, jfieldID, jint);
  void (*SetStaticLongField)(JNIEnv *, jclass, jfieldID, jlong);
  void (*SetStaticFloatField)(JNIEnv *, jclass, jfieldID, jfloat);
  void (*SetStaticDoubleField)(JNIEnv *, jclass, jfieldID, jdouble);

字符串JNI相关转换函数

  /**
   * 创建一个新的java.lang.String对象
   * @param env JNI接口指针
   * @param unicodeChars 指向Unicode字符串的指针
   * @param len Unicode字符串的长度
   * @return String对象 or NULL
   *
   * @throws OutOfMemoryError 内存不足
   */
  jstring (*NewString)(JNIEnv *, const jchar *, jsize);


  /**
   * 返回java.lang.String的长度(Unicode字符数)
   * @param env JNI接口指针
   * @param string String对象
   * @return 长度
   */
  jsize (*GetStringLength)(JNIEnv *, jstring);
  
  /**
   * 返回指向Unicode字符数组的指针
   * 该指针在调用ReleaseStringchars()前一直有效
   * 如果isCopy非空,则在复制完成后将*isCopy设为JNI_TRUE。否则设为JNI_FALSE
   * @param env JNI接口指针
   * @param string String对象
   * @param isCopy 指向boolean的指针
   * @return 指向字符串的指针 or NULL
   */
  const jchar *(*GetStringChars)(JNIEnv *, jstring, jboolean *);
  
  /**
   * 通知VM无需再访问chars
   * chars是一个指针,通过GetStringChars()
   *
   * @param env JNI接口指针
   * @param string String对象
   * @param chars 指向字符串的指针
   */
  void (*ReleaseStringChars)(JNIEnv *, jstring, const jchar *);
  
  /**
   * 根据UTF-8编码的字符数组创建一个新的java.lang.String对象
   * @param env JNI接口指针
   * @param bytes 指向UTF-8字符串的指针
   * @return String对象 or NULL
   *
   * @throws OutOfMemoryError 内存不足
   */
  jstring (*NewStringUTF)(JNIEnv *, const char *);
  
  /**
   * 返回字符串以UTF-8为编码的字节数
   *
   * @param env JNI接口指针
   * @param string String对象
   *
   * @return 字符串的UTF-8字节数
   */
  jsize (*GetStringUTFLength)(JNIEnv *, jstring);
  
  /**
   * 返回指向UTF-8编码字符数组的指针
   * 该指针在调用ReleaseStringUTFChars()前一直有效
   * 如果isCopy非空,则在复制完成后将*isCopy设为JNI_TRUE。否则设为JNI_FALSE
   * @param env JNI接口指针
   * @param string String对象
   * @param isCopy 指向boolean的指针
   * @return 指向字符串的指针 or NULL
   */
  const char *(*GetStringUTFChars)(JNIEnv *, jstring, jboolean *);
  
  /**
   * 通知VM无需再访问utf
   * utf是一个指针,通过GetStringUTFChars()
   * @param env JNI接口指针
   * @param string String对象
   * @param utf 指向字符串的指针
   */
  void (*ReleaseStringUTFChars)(JNIEnv *, jstring, const char *);

数组JNI相关转换函数

  /**
   * 获取数组元素个数
   * @param env JNI接口指针
   * @param array java数组对象
   * @return 数组长度
   */
  jsize (*GetArrayLength)(JNIEnv *, jarray);


  /**
   * 创建新的elementClass类型数组,所有元素初始值均设为initialElement
   * @param env JNI接口指针
   * @param length 数组大小
   * @param elementClass 数组类型
   * @param initialElement 初始值
   * @return 数组对象 or NULL
   */
  jobjectArray (*NewObjectArray)(JNIEnv *, jsize, jclass, jobject);
  
  /**
   * 获取对象数组中指定index的值
   * @param env JNI接口指针
   * @param array java数组
   * @param index 索引
   * @return 索引对象的对象
   *
   * @throws ArrayIndexOutOfBoundsException
   */
  jobject (*GetObjectArrayElement)(JNIEnv *, jobjectArray, jsize);
  
  /**
   * 设置对象数组中指定index的值
   * @param env JNI接口指针
   * @param array java数组
   * @param index 索引
   * @param value 新的值
   * @throws ArrayIndexOutOfBoundsException
   */
  void (*SetObjectArrayElement)(JNIEnv *, jobjectArray, jsize, jobject);
  
  /**
   * ArrayType NewArray(JNIEnv *env, jsize length);
   * 创建基本类型数组对象
   * @param env JNI接口指针
   * @param length 数组大小
   * @return 数组对象 or NULL
   */
  jbooleanArray (*NewBooleanArray)(JNIEnv *, jsize);
  jbyteArray (*NewByteArray)(JNIEnv *, jsize);
  jcharArray (*NewCharArray)(JNIEnv *, jsize);
  jshortArray (*NewShortArray)(JNIEnv *, jsize);
  jintArray (*NewIntArray)(JNIEnv *, jsize);
  jlongArray (*NewLongArray)(JNIEnv *, jsize);
  jfloatArray (*NewFloatArray)(JNIEnv *, jsize);
  jdoubleArray (*NewDoubleArray)(JNIEnv *, jsize);


  /**
   * NativeType *GetArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);
   * 返回基本类型数组中的数据,通过返回的指针可以访问这些数据,若虚拟机支持pinning,则指针指向原始数组,否则指向原始数组的拷贝
   * 返回的指针在ReleaseArrayElements()调用前一直有效
   * 数组用使用结束后,调用ReleaseArrayElements,并在调用参数中决定是否把修改提交给java
   * @param env JNI接口指针
   * @param array java数组
   * @param isCopy 指向boolean的指针,若不为NULL,则执行了复制设为JNI_TRUE,否则设为JNI_FALSE
   * @return 指向数组元素的指针 or NULL
   */
  jboolean *(*GetBooleanArrayElements)(JNIEnv *, jbooleanArray, jboolean *);
  jbyte *(*GetByteArrayElements)(JNIEnv *, jbyteArray, jboolean *);
  jchar *(*GetCharArrayElements)(JNIEnv *, jcharArray, jboolean *);
  jshort *(*GetShortArrayElements)(JNIEnv *, jshortArray, jboolean *);
  jint *(*GetIntArrayElements)(JNIEnv *, jintArray, jboolean *);
  jlong *(*GetLongArrayElements)(JNIEnv *, jlongArray, jboolean *);
  jfloat *(*GetFloatArrayElements)(JNIEnv *, jfloatArray, jboolean *);
  jdouble *(*GetDoubleArrayElements)(JNIEnv *, jdoubleArray, jboolean *);
  
  /**
   * ReleaseArrayElements
   * 通知VM不再需要访问这些数组,根据mode参数的不同,将决定是否把数组的修改复制到源数组
   *
   * @param env JNI接口指针
   * @param array java数组对象
   * @param elems 指向数组元素的指针
   * @param mode 释放模式,0:把数据复制回源数组并释放elems缓冲区,JNI_COMMIT:把数据复制回源数组但不释放elems缓冲区,JNI_ABORT:不把数据复制回源数组,释放elems缓冲区
   */
  void (*ReleaseBooleanArrayElements)(JNIEnv *, jbooleanArray, jboolean *, jint);
  void (*ReleaseByteArrayElements)(JNIEnv *, jbyteArray, jbyte *, jint);
  void (*ReleaseCharArrayElements)(JNIEnv *, jcharArray, jchar *, jint);
  void (*ReleaseShortArrayElements)(JNIEnv *, jshortArray, jshort *, jint);
  void (*ReleaseIntArrayElements)(JNIEnv *, jintArray, jint *, jint);
  void (*ReleaseLongArrayElements)(JNIEnv *, jlongArray, jlong *, jint);
  void (*ReleaseFloatArrayElements)(JNIEnv *, jfloatArray, jfloat *, jint);
  void (*ReleaseDoubleArrayElements)(JNIEnv *, jdoubleArray, jdouble *, jint);
  
  /**
   * void GetArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
   * 把基本类型数组拷贝到buf中
   * @param env JNI接口指针
   * @param array java数组
   * @param start 开始index
   * @param len 拷贝长度
   * @param buf 目标地址
   * @throws ArrayIndexOutOfBoundsException
   */
  void (*GetBooleanArrayRegion)(JNIEnv *, jbooleanArray, jsize, jsize, jboolean *);
  void (*GetByteArrayRegion)(JNIEnv *, jbyteArray, jsize, jsize, jbyte *);
  void (*GetCharArrayRegion)(JNIEnv *, jcharArray, jsize, jsize, jchar *);
  void (*GetShortArrayRegion)(JNIEnv *, jshortArray, jsize, jsize, jshort *);
  void (*GetIntArrayRegion)(JNIEnv *, jintArray, jsize, jsize, jint *);
  void (*GetLongArrayRegion)(JNIEnv *, jlongArray, jsize, jsize, jlong *);
  void (*GetFloatArrayRegion)(JNIEnv *, jfloatArray, jsize, jsize, jfloat *);
  void (*GetDoubleArrayRegion)(JNIEnv *, jdoubleArray, jsize, jsize, jdouble *);

  /**
   * void SetArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, const NativeType *buf);
   * 把buf中的内容拷贝回数组中
   * @param env JNI接口指针
   * @param array java数组
   * @param start 开始index
   * @param len 拷贝长度
   * @param buf 源数据
   * @throws ArrayIndexOutOfBoundsException
   */
  void (*SetBooleanArrayRegion)(JNIEnv *, jbooleanArray, jsize, jsize, const jboolean *);
  void (*SetByteArrayRegion)(JNIEnv *, jbyteArray, jsize, jsize, const jbyte *);
  void (*SetCharArrayRegion)(JNIEnv *, jcharArray, jsize, jsize, const jchar *);
  void (*SetShortArrayRegion)(JNIEnv *, jshortArray, jsize, jsize, const jshort *);
  void (*SetIntArrayRegion)(JNIEnv *, jintArray, jsize, jsize, const jint *);
  void (*SetLongArrayRegion)(JNIEnv *, jlongArray, jsize, jsize, const jlong *);
  void (*SetFloatArrayRegion)(JNIEnv *, jfloatArray, jsize, jsize, const jfloat *);
  void (*SetDoubleArrayRegion)(JNIEnv *, jdoubleArray, jsize, jsize, const jdouble *);

结语

接触JNI的时间也不长,希望未来可以更好的突破吧,更快的提升吧,fighting~

你可能感兴趣的:(jni,java,ndk,ndk,jni)