文章目录- I . jintArray 类型说明
- II . jboolean 类型说明
- III . GetIntArrayElements 方法解析 ( jintArray -> jint* | int* )
- IV . jarray 类型说明
- V . GetArrayLength 方法解析 ( 获取 jarray 数组长度 )
- VI . 日志打印
- VII . 遍历 int 数组
- VIII . ReleaseIntArrayElements 方法说明 ( 释放 C/C 中的 int 数组 )
- IX . 完整代码示例
I . jintArray 类型说明
1 . C 环境类型定义 : 下面是 jintArray 类型的定义 , jintArray 的本质是一个 _jobject 类对象指针 ;
代码语言:javascript复制class _jobject {}; // 定义 _jobject 类 , 这是一个空类
class _jarray : public _jobject {}; // 定义 _jarray 类 继承 _jobject 类
class _jintArray : public _jarray {}; // 定义 _jintArray 类 , 继承 _jarray 类
typedef _jintArray* jintArray; // 定义 _jintArray* 别名 , jintArray
public 继承 : 父类成员在子类中访问级别不变
2 . jintArray 类型本质 : jintArray 是一个 _jintArray 类的指针 , 其 _jintArray 类 对象存储在内存中 , _jintArray * 指针指向该内存 ;
为 _jintArray * 指针变量类型 定义别名 jintArray 类型 ;
3 . ( jintArray -> jint * ) 类型转换 : 这是 Java 中的 int 数组的内存地址 , 如果要在 C/C 环境中使用 , 要将该 jintArray 类型变量转为 jint* 类型的变量 ;
使用 jint* GetIntArrayElements(jintArray array, jboolean* isCopy) 方法 , 可以实现上述转化 ( jintArray -> jint * ) ;
II . jboolean 类型说明
1 . C 环境类型定义 : 下面是 jboolean 类型的定义 , jboolean 本质是 无符号 char 类型 ;
代码语言:javascript复制typedef unsigned char __uint8_t; // 定义 char 类型别名 __uint8_t
typedef __uint8_t uint8_t; // 定义 __uint8_t 类型别名 uint8_t
typedef uint8_t jboolean; // 定义 uint8_t 类型别名 jboolean
2 . jboolean 类型取值 : jboolean 的取值只能是 0 和 1 , 也可以使用 JNI_FALSE 和 JNI_TRUE 宏定义 ;
代码语言:javascript复制#define JNI_FALSE 0
#define JNI_TRUE 1
III . GetIntArrayElements 方法解析 ( jintArray -> jint* | int* )
1 . GetIntArrayElements 函数作用 : 将 Java 环境的 int 数组类型变量 ( jintArray 类型 ) , 转为 C/C 环境中的 jint 数组指针 , 返回一个指针指向 jint 数组首元素地址 ;
jint 本质就是 int 类型 , GetIntArrayElements 函数作用就是将 jintArray 转为 int* 指针 ;
2 . GetIntArrayElements 函数原型 :
代码语言:javascript复制struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
{
// 调用 JNINativeInterface 结构体中封装的 GetIntArrayElements 方法
return functions->GetIntArrayElements(this, array, isCopy);
}
...
}
3 . jintArray array 参数 : 该参数是从 Java 层调用传入的参数 , jintArray 的本质是一个 _jobject 类对象指针 ;
4 . jboolean* isCopy 参数 : 该参数用于指定将 jintArray 类型的变量 , 转为 jint * 指针类型的变量 , 新的指针变量的生成方式 ;
① 将 该参数设置成指向 JNI_TRUE 的指针 : 将 int 数组数据拷贝到一个新的内存空间中 , 并将该内存空间首地址返回 ;
② 将 该参数设置成指向 JNI_FALSE 的指针 : 直接使用 java 中的 int 数组地址 , 返回 java 中的 int 数组的首地址 ;
③ 将 该参数设置成 NULL ( 推荐 ) : 表示不关心如何实现 , 让系统自动选择指针生成方式 , 一般情况下都不关心该生成方式 ;
5 . 代码示例 :
① jboolean* isCopy 参数 准备 代码示例 : jboolean 类型变量可取值 JNI_FALSE 0 和 JNI_TRUE 1 两个值 ;
代码语言:javascript复制 jboolean isCopy = JNI_TRUE;
② GetIntArrayElements 方法调用代码示例 :
intArray_ 是 Java 层传入的 jintArray intArray_ 参数变量 ; JNIEnv *env 是 JNI 方法的默认参数 , 这里是 C 环境中的 JNIEnv 指针类型 ; jboolean* isCopy 设置成 NULL 参数表示 不关心 jint* 类型变量的生成方式 ;
代码语言:javascript复制 jint *intArray = env->GetIntArrayElements(intArray_, NULL);
如果是其它基础数据类型的数组 , 将 Get***ArrayElements 方法名中的 基础数据类型修改一下即可 ;
- 如果是布尔类型的数组 , 使用 GetBooleanArrayElements 方法 ;
- 如果是浮点型的数组 , 使用 GetFloatArrayElements 方法 ;
- 如果是字符型的数组 , 使用 GetCharArrayElements 方法 ;
IV . jarray 类型说明
1 . jarray 类型 : 该类型的本质是一个指针 , 指向一个空对象地址 , 这个对象一般是从 Java 层传递进来 ;
代码语言:javascript复制class _jobject {}; // 定义 _jobject 空类
class _jarray : public _jobject {}; // 定义 _jarray 类 , 继承 _jobject 类
typedef _jarray* jarray; // 定义 _jarray* 指针类型 别名为 jarray
2 . _jarray 类子类 : 下面定义的 9 个类 , 都是 _jarray 子类 , 都可以使用 GetArrayLength 方法获取数组长度 ;
代码语言:javascript复制class _jarray : public _jobject {};
//下面定义的都是 jarray 子类
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
3 . Java 传入的数组类型别名 : 下面定义的 9 个别名 , 本质上都是 _jarray 类型对象 或者 其子类对象的 指针 , 即 _jarray* 类型 ;
代码语言:javascript复制typedef _jarray* jarray;
//下面是 9 个是 Java 传入的数组类型别名
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
V . GetArrayLength 方法解析 ( 获取 jarray 数组长度 )
1 . 函数作用 : 获取 jarray 数组长度 , 该 jarray 类型可以是下面定义的类型 ;
代码语言:javascript复制typedef _jarray* jarray;
//下面是 9 个是 Java 传入的数组类型别名
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
2 . 函数原型 :
代码语言:javascript复制struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
jsize GetArrayLength(jarray array)
{
调用 JNINativeInterface 结构体中封装的 GetArrayLength 方法
return functions->GetArrayLength(this, array);
}
...
}
3 . 返回值类型说明 : jsize 类型本质还是 int 类型 ;
代码语言:javascript复制typedef int __int32_t;
typedef __int32_t int32_t;
typedef int32_t jint;
typedef jint jsize;
4 . 函数调用示例 : 其中的 jintArray intArray_ 是 Java 层传入的参数 ;
代码语言:javascript复制jsize len = env->GetArrayLength(intArray_);
VI . 日志打印
1 . 日志库配置 :
① 导入日志库 : #include <android/log.h>
② CMake 设置日志库 : add_library 设置动态库名称 , find_library 中为 查找日志库 , target_link_libraries 连接日志库 ;
代码语言:javascript复制add_library(
native-lib
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
native-lib
${log-lib})
2 . 日志打印函数函数原型 :
代码语言:javascript复制int __android_log_print(int prio, const char* tag, const char* fmt, ...)
3 . 日志打印函数参数说明 :
- ① int prio 参数 : 日志的等级 , 定义在 log.h 的 android_LogPriority 枚举中 ;
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
- ② const char* tag 参数 : 日志打印的 TAG 标签 , 这是一个 C/C char* 类型字符串 ;
- ③ const char* fmt, … 参数 : 可变参数 ;
4 . 日志打印函数代码示例 :
代码语言:javascript复制 /*
__android_log_print 打印 Android 日志函数
函数原型 : int __android_log_print(int prio, const char* tag, const char* fmt, ...)
int prio 参数 : 日志的等级 , 定义在 jni.h 的 android_LogPriority 枚举中
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
const char* tag 参数 : 日志打印的 TAG 标签 , 这是一个 C/C char* 类型字符串
const char* fmt, ... 参数 : 可变参数
*/
__android_log_print(ANDROID_LOG_INFO, "JNI_TAG" , "%d . %d" , i , *num);
VII . 遍历 int 数组
1 . 使用指针遍历 jint 数组 : jint *intArray ;
intArray 是数组首元素地址 intArray 1 是第 1 个元素的首地址 intArray k 是第 k 个元素的首地址
2 . 函数调用 代码示例 :
代码语言:javascript复制 /*
使用指针进行访问
intArray 是数组首元素地址
intArray 1 是第 1 个元素的首地址
intArray k 是第 k 个元素的首地址
使用 *(intArray k) 可以获取第 k 个元素的值
*/
for(int i = 0; i < len; i ){
//获取第 i 个元素的首地址 , 使用 *num 可以获取第 i 个元素的值
int *num = intArray i;
/*
__android_log_print 打印 Android 日志函数
函数原型 : int __android_log_print(int prio, const char* tag, const char* fmt, ...)
int prio 参数 : 日志的等级 , 定义在 jni.h 的 android_LogPriority 枚举中
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
const char* tag 参数 : 日志打印的 TAG 标签 , 这是一个 C/C char* 类型字符串
const char* fmt, ... 参数 : 可变参数
*/
__android_log_print(ANDROID_LOG_INFO, "JNI_TAG" , "%d . %d" , i , *num);
//修改数组中的值
*num = 8888;
}
VIII . ReleaseIntArrayElements 方法说明 ( 释放 C/C 中的 int 数组 )
1 . 函数作用 : 释放 C/C 中的 jint 数组 , 设置 jintArray array 类型的返回模式 ;
2 . 函数原型 :
代码语言:javascript复制struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
void ReleaseIntArrayElements(jintArray array, jint* elems,
jint mode)
//调用的是 JNINativeInterface 结构体中封装的 ReleaseIntArrayElements 方法
{ functions->ReleaseIntArrayElements(this, array, elems, mode); }
...
}
3 . ReleaseIntArrayElements 参数解析 :
① jintArray array 参数 : Java 层传入的 数组参数 ; ② jint* elems 参数 : 使用 GetIntArrayElements 方法 Java 的 int 数组 C/C 中 ③ jint mode 参数 : 设置处理模式 , 有三种处理模式 ;
4 . ReleaseIntArrayElements 方法 jint mode 参数 详解 :
① 模式 0 : 刷新 Java 数组 , 释放 C/C 数组 ② 模式 1 ( JNI_COMMIT ) : 刷新 Java 数组 , 不释放 C/C 数组 ③ 模式 2 ( JNI_ABORT ) : 不刷新 Java 数组 , 释放 C/C 数组
下面是 jni.h 中的定义的模式 :
代码语言:javascript复制#define JNI_COMMIT 1
#define JNI_ABORT 2
如果设置 0 和 1 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值会被修改 如果设置 2 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值不会被修改
IX . 完整代码示例
1 . Java 代码 :
代码语言:javascript复制package kim.hsl.jni;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1 . 系统生成的方法
// 调用 stringFromJNI 方法 , 显示从 Native 层传入的字符串
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
//2 . 测试 字符串
jniTest(888 , "字符串测试");
//3 . 测试 int 数组 和 字符串数组
//准备 int 数组 和 String 数组
int[] intArray = {1 , 2 , 666 , 888 , 95555};
String[] stringArray = {"Hello" , "World" , "Hanshuliang"};
jniArrayTest(intArray, stringArray);
//打印 int 数组
/*
void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode)
第三个参数 mode :
① 如果设置 0 和 1 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值会被修改
② 如果设置 2 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值不会被修改
*/
Log.i("JNI_TAG" , "Java 层 jniArrayTest 执行完毕后 , int[] intArray 数组内容 : " Arrays.toString(intArray) );
}
/**
* 系统自动生成的 JNI 方法
*/
public native String stringFromJNI();
/**
* 传入基本类型参数 和 字符串类型参数
* @param i
* @param s
*/
public native void jniTest(int i, String s);
/**
* 传入数组对象给 Native 层
* @param intArray
* @param stringArray
*/
public native void jniArrayTest(int[] intArray , String[] stringArray);
}
2 . C 代码 :
代码语言:javascript复制#include <jni.h>
#include <string>
//导入日志库
#include <android/log.h>
//定义日志宏 , 其中的 __VA_ARGS__ 表示可变参数
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"JNI",__VA_ARGS__);
extern "C"
JNIEXPORT jstring JNICALL
Java_kim_hsl_jni_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
// 创建 C 字符串
std::string hello = "Hello from C ";
// 返回 jstring 类型的字符串
// 将 C/C 的 char* 字符串转为 Java 中的 jstring 类型字符串
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniTest(JNIEnv *env, jobject instance, jint i, jstring s_) {
// 将 jstring 类型数据转为 char 类型数据
const char *s = env->GetStringUTFChars(s_, 0);
// 释放
env->ReleaseStringUTFChars(s_, s);
}
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniArrayTest(JNIEnv *env, jobject instance, jintArray intArray_,
jobjectArray stringArray) {
// I . 基本类型数组操作
// 1 . jboolean 类型
/*
jboolean 类型的值可以设置成 true 或 false , 也可以不设置
如果将值传递给 GetIntArrayElements 方法 , 需要将 isCopy 的地址放在第二个参数位置
当做参数的格式 : env->GetIntArrayElements(intArray_, &isCopy);
可取值 JNI_FALSE 0 和 JNI_TRUE 1 两个值
*/
jboolean isCopy = JNI_TRUE;
//2 . GetIntArrayElements 方法参数解析
/*
GetIntArrayElements 方法参数解析
方法作用 : 将 Java 的 int 数组 , 转为 jint 数组 , 返回一个指针指向 jint 数组首元素地址
函数原型 : jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
第一个参数 : jintArray array 是参数中的 jintArray 类型变量
jintArray 类型说明 :
class _jobject {}; C 中定义了 _jobject 类
class _jarray : public _jobject {}; 定义 _jarray 类 继承 _jobject 类
public 继承 : 父类成员在子类中访问级别不变
class _jintArray : public _jarray {}; 定义 _jintArray 类 继承 _jarray 类
typedef _jintArray* jintArray; 将 _jintArray* 类型 声明成 jintArray 类型
第二个参数 : jboolean* isCopy
该参数用于指定将 jintArray 类型的变量 , 转为 jint * 指针类型的变量 , 新的指针变量的生成方式
将 该参数设置成指向 JNI_TRUE 的指针 : 将 int 数组数据拷贝到一个新的内存空间中 , 并将该内存空间首地址返回
将 该参数设置成指向 JNI_FALSE 的指针 : 直接使用 java 中的 int 数组地址 , 返回 java 中的 int 数组的首地址
将 该参数设置成 NULL ( 推荐 ) : 表示不关心如何实现 , 让系统自动选择指针生成方式 , 一般情况下都不关心该生成方式
注意如果是 其它类型的数组
如果是布尔类型的数组 , 使用 GetBooleanArrayElements 方法
如果是浮点型的数组 , 使用 GetFloatArrayElements 方法
如果是字符型的数组 , 使用 GetCharArrayElements 方法
...
*/
jint *intArray = env->GetIntArrayElements(intArray_, NULL);
//3 . 操作 jint * 指针变量 , 循环获取数组中每个元素的值
/*
获取数组长度
函数原型 : jsize GetArrayLength(jarray array)
返回值类型 jsize :
jsize 类型 : 由下面可知 jsize 只是 int 类型的别名
typedef jint jsize;
typedef int32_t jint;
typedef __int32_t int32_t;
typedef int __int32_t;
*/
jsize len = env->GetArrayLength(intArray_);
//4 . 循环打印 int 数组中的元素
/*
使用指针进行访问
intArray 是数组首元素地址
intArray 1 是第 1 个元素的首地址
intArray k 是第 k 个元素的首地址
使用 *(intArray k) 可以获取第 k 个元素的值
*/
for(int i = 0; i < len; i ){
//获取第 i 个元素的首地址 , 使用 *num 可以获取第 i 个元素的值
int *num = intArray i;
/*
__android_log_print 打印 Android 日志函数
函数原型 : int __android_log_print(int prio, const char* tag, const char* fmt, ...)
int prio 参数 : 日志的等级 , 定义在 jni.h 的 android_LogPriority 枚举中
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
const char* tag 参数 : 日志打印的 TAG 标签 , 这是一个 C/C char* 类型字符串
const char* fmt, ... 参数 : 可变参数
*/
__android_log_print(ANDROID_LOG_INFO, "JNI_TAG" , "%d . %d" , i , *num);
//修改数组中的值
*num = 8888;
}
//5 . 释放 jint * 类型的指针变量
/*
函数原型 : void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode)
第一参数 jintArray array : 是 Java 层传入的 int 数组 参数 , 即 Native 层的调用函数的参数
第二参数 jint* elems : 通过 GetIntArrayElements 方法将 jintArray 变量转成的 jint* 变量
第三参数 jint mode : 设置处理模式 , 有三种处理模式
模式 0 : 刷新 Java 数组 , 释放 C/C 数组
模式 1 ( JNI_COMMIT ) : 刷新 Java 数组 , 不释放 C/C 数组
模式 2 ( JNI_ABORT ) : 不刷新 Java 数组 , 释放 C/C 数组
下面是 jni.h 中的定义的模式 :
#define JNI_COMMIT 1 copy content, do not free buffer
#define JNI_ABORT 2 free buffer w/o copying back
如果设置 0 和 1 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值会被修改
如果设置 2 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值不会被修改
*/
env->ReleaseIntArrayElements(intArray_, intArray, 0);
}
3 . 执行结果
代码语言:javascript复制01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: 0 . 1
01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: 1 . 2
01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: 2 . 666
01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: 3 . 888
01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: 4 . 95555
01-12 16:51:56.594 7411-7411/kim.hsl.jni I/JNI_TAG: Java 层 jniArrayTest 执行完毕后 , int[] intArray 数组内容 : [8888, 8888, 8888, 8888, 8888]