【Android NDK 开发】JNI 方法解析 ( int 数组传递 | jintArray 类型 | 数组转换 | 获取数组长度 | 获取数组元素 | 指针遍历数组 | 数组返回值设置 )(二)

简介: 【Android NDK 开发】JNI 方法解析 ( int 数组传递 | jintArray 类型 | 数组转换 | 获取数组长度 | 获取数组元素 | 指针遍历数组 | 数组返回值设置 )(二)

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]


目录
相关文章
|
人工智能
歌词结构的巧妙安排:写歌词的方法与技巧解析,妙笔生词AI智能写歌词软件
歌词创作是一门艺术,关键在于巧妙的结构安排。开头需迅速吸引听众,主体部分要坚实且富有逻辑,结尾则应留下深刻印象。《妙笔生词智能写歌词软件》提供多种 AI 功能,帮助创作者找到灵感,优化歌词结构,写出打动人心的作品。
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
244 3
|
11月前
|
监控 安全 网络安全
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
1605 89
|
存储 Java 开发者
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
533 132
|
人工智能
写歌词的技巧和方法全解析:开启你的音乐创作之旅,妙笔生词智能写歌词软件
怀揣音乐梦想,渴望用歌词抒发情感?掌握关键技巧,你也能踏上创作之旅。灵感来自生活点滴,主题明确,语言简洁,韵律和谐。借助“妙笔生词智能写歌词软件”,AI辅助创作,轻松写出动人歌词,实现音乐梦想。
|
安全 Ubuntu Shell
深入解析 vsftpd 2.3.4 的笑脸漏洞及其检测方法
本文详细解析了 vsftpd 2.3.4 版本中的“笑脸漏洞”,该漏洞允许攻击者通过特定用户名和密码触发后门,获取远程代码执行权限。文章提供了漏洞概述、影响范围及一个 Python 脚本,用于检测目标服务器是否受此漏洞影响。通过连接至目标服务器并尝试登录特定用户名,脚本能够判断服务器是否存在该漏洞,并给出相应的警告信息。
741 84
|
10月前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
679 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
9月前
|
Python Windows
[oeasy]python076_int这个词怎么来的_[词根溯源]整数类型_int_integer_touch
本文探讨了“int”一词的起源及其与整数类型的关联。通过词根溯源,揭示“int”来源于“integer”,意为“完整的数”,与零碎的分数相对。同时分析了相关词汇如“tact”(接触)、“touch”(触摸)及衍生词,如“tangential”(切线的)、“intagible”(无形的)和“integral”(完整的、不可或缺的)。文章还结合编程语言特性,解释了Python作为动态类型、强类型语言的特点,并总结了整型变量的概念与意义。最后预告了后续内容,提供了学习资源链接。
333 11
|
9月前
|
存储 C语言 Python
[oeasy]python077_int类型怎么用_整数运算_integer_进制转化_int类
本文主要讲解了Python中`int`类型的应用与特性。首先回顾了`int`词根的溯源,探讨了整型变量的概念及命名规则(如匈牙利命名法)。接着分析了整型变量在内存中的存储位置和地址,并通过`type()`和`id()`函数验证其类型和地址。还介绍了整型变量的运算功能,以及如何通过`int()`函数将字符串转化为整数,支持不同进制间的转换(如二进制转十进制)。此外,文章提及了关键字`del`的使用场景,对比了Python与C语言中`int`的区别,并总结了整型与字符串类型的差异,为后续深入学习奠定基础。
213 1
|
9月前
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。

推荐镜像

更多