VIII . ReleaseIntArrayElements 方法说明 ( 释放 C/C++ 中的 int 数组 )
1 . 函数作用 : 释放 C/C++ 中的 jint 数组 , 设置 jintArray array 类型的返回模式 ;
2 . 函数原型 :
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 中的定义的模式 :
#define JNI_COMMIT 1 #define JNI_ABORT 2
如果设置 0 和 1 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值会被修改
如果设置 2 , 那么 如果修改了 int 数组的值 , 那么最终 Java 层的值不会被修改
IX . 完整代码示例
1 . Java 代码 :
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++ 代码 :
#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 . 执行结果
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]