JNI编程二:JNI数据类型

目录

  • 前言
  • 一、数据类型 jclass / jobject
  • 二、JNI常见的数据类型
  • 三、运用数据类型
    • 3.1 修改String类型的变量
    • 3.2 修改int类型的变量


前言

前面阐述了JNI的开发流程,接下来探究JNI中的数据类型。编码承接上文JNI编程一:JNI开发流程

一、数据类型 jclass / jobject

在jni里面会有两个这样的数据类型jclass / jobject,顾名思义它们分别对应java里面的 Class和Object。

现在我们来编写对应的代码,之前创建的java工程里面有一个类JniMain.java,声明了第一个native方法。

public class JniMain {//静态方法public native static String getStringFromC();static{System.loadLibrary("JNI_Demo1");}public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(getStringFromC());}
}

声明的 public native static String getStringFromC() 这个方法是一个静态方法,这也就意味着我们可以不需要创建JniMain类对象而直接调用。

那么再来看看通过javah编译生成的JniMain.h文件

#include "jni.h"#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     JniMain* Method:    getStringFromC* Signature: ()Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC (JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif

可以看到Java_JniMain_getStringFromC(JNIEnv*,jclass)函数里面有一个参数是jclass类型的。

再来看看实现JniMain.h文件函数的jni_impl.c

#include "stdafx.h"#include "JniMain.h"JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC (JNIEnv * env, jclass jclz){return (*env)->NewStringUTF(env, "hello JNI");
}

函数JNICALL Java_JniMain_getStringFromC (JNIEnv * env, jclass jclz)里面的第二个参数是jclass类型,形参jclz就是对应的JniMain.class对象。

也就是说当我们在java文件里面编写native方法是一个static修饰的方法,那么我们jni头文件里面对应的方法就会添加一个jclass类型的形参。

那么在java文件里面编写native方法是一个非static修饰的方法还会有jclass参数吗?
接下来在JniMain.java里面编写一个非静态native方法 String getStringFromC2()

public class JniMain {//静态方法public native static String getStringFromC();//非静态方法public native String getStringFromC2();
}

再来看一看编译生成的JniMain.h文件

#include <jni.h>#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     JniMain* Method:    getStringFromC* Signature: ()Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC(JNIEnv *, jclass);/** Class:     JniMain* Method:    getStringFromC2* Signature: ()Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

这个时候Java_JniMain_getStringFromC2(JNIEnv *, jobject)函数的形参就变成了jobject类型的了。

接下来我们再来看看jni_impl.c里面对该函数的实现

#include "stdafx.h"#include "JniMain.h"JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
(JNIEnv * env, jclass jclz){return (*env)->NewStringUTF(env, "hello JNI");
}JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2
(JNIEnv * env, jobject jobj){return (*env)->NewStringUTF(env, "I'm stonger");
}

可以看到Java_JniMain_getStringFromC2(JNIEnv * env, jobject jobj)函数里面的形参是jobject类型的,形参jobj就是对应的java里面的JniMain对象。

最后我们将c工程再编译生成一个JNI_Demo1.dll动态库,将它放到java工程里面运行。
附上JniMain.java调用动态库的代码

public class JniMain {//静态方法public native static String getStringFromC();//非静态方法public native String getStringFromC2();static{System.loadLibrary("JNI_Demo1");}public static void main(String[] args) {//调用静态native方法System.out.println(getStringFromC());//调用非静态native方法JniMain jm = new JniMain();System.out.println(jm.getStringFromC2());}}

运行结果如下

在这里插入图片描述


二、JNI常见的数据类型

现在java JNI c所对应的基本数据类型
java      JNI      c
基本数据类型:
boolean    jboolean   unsigned char
byte      jbyte    signed char
char      jchar     signed char
short     jshort      short
int       jint     int
long      jlong     long long
float      jfloat    float
double    double    double
引用类型:
String      jstring
Object      jobject
基本数据类型数组:
byte[]      jByteArray
引用数据类型数组:
Object[]    jobjectArray
String[]    jobjectArray

三、运用数据类型

接下来,咱们来运用运用jin数据类型。

3.1 修改String类型的变量

举个例子,在java代码中有一个String类型的变量,我们通过jni去修改这个变量。

现在我们开始写java代码,JniMain里面声明了key字符串和accessFieldModify()方法

public class JniMain {//一个全局的字符串public String key = "key";//修改key的方法public native String accessFieldModify();public static void main(String[] args) {}}

我们用javah编译JniMain.java,将生成好的JniMain.h放到c工程里面,下面展示JniMain.h代码

#include <jni.h>#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     JniMain* Method:    accessFieldModity* Signature: ()Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_JniMain_accessFieldModity(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

接下来我们就在jni_impl.c文件里面实现这个Java_JniMain_accessFieldModity(JNIEnv *, jobject)方法,这个方法的功能就是修改JniMain.java里面全局变量key的值。实现代码如下

#include "stdafx.h"#include "JniMain.h"
#include <string.h>//访问java中非静态全局变量key,并修改值
JNIEXPORT jstring JNICALL Java_JniMain_accessFieldModity
(JNIEnv * env, jobject jobj){//得到jclass,即JniMain.classjclass jclz = (*env)->GetObjectClass(env,jobj);//得FieldId, "key" 属性名称,"Ljava/lang/String;"属性签名jfieldID fid = (*env)->GetFieldID(env,"jclz", "key", "Ljava/lang/String;");//得到key对应的值jstring jstr = (*env)->GetObjectField(env,jobj,fid);//将jstring类型转化为c语言的char字符数组char * c_str = (*env)->GetStringUTFChars(env,jstr,NULL);//将字符串"key"改成"hello key"char text[20] = "hello ";strcat(text, c_str);jstring new_str = (*env)->NewStringUTF(env,text);//给jobj的key成员变量设置新的值(*env)->SetObjectField(env,jobj,fid,new_str);//释放内存(*env)->ReleaseStringUTFChars(env,new_str,c_str);return new_str;
}

上述代码中的jni方法太纠结,这些方法的作用会在以后的博客中介绍到,现在只需要结合注释明白Java_JniMain_accessFieldModity方法里面的实现过程即可。其中获取域jfieldID的方法有个属性签名"Ljava/lang/String;",这个签名是标识这个域在在java里面对应的什么数据类型,"Ljava/lang/String;"就代表的是String类型。
关于属性前面有一个对应的表,在平时编写jni时对应查阅一下就行了。
在这里插入图片描述
在这里插入图片描述

编写完jni代码之后就生成对应的.dll动态库(如何生成动态库,在上一篇博客里面有讲到),再将动态库放到java工程里面调用。
贴出JniMain.java里面的调用代码:

public class JniMain {//一个全局的字符串public String key = "key";//修改key的方法public native String accessFieldModify();static{System.loadLibrary("JNI_Demo1");}public static void main(String[] args) {//调用非静态native方法JniMain jm = new JniMain();System.out.println("change before key: "+jm.key);jm.accessFieldModify();System.out.println("after change key: "+jm.key);}}

再来看看运行结果,成功修改了成员变量key的值
在这里插入图片描述

3.2 修改int类型的变量

再举个例子,在java里面定义一个int类型的静态成员变量,然后通过jni去修改这个变量。
首先编写我们的JniMain.java代码

public class JniMain {public static int count = 2;public native void accessStaticFieldModify();
}

接下来,生成JniMain.h,并把JniMain.h文件放到c工程里面

#include "jni.h"#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif/** Class:     JniMain* Method:    accessStaticFieldModify* Signature: ()V*/
JNIEXPORT void JNICALL Java_JniMain_accessStaticFieldModify(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

然后再在jni_impl.c里面实现Java_JniMain_accessStaticFieldModify(JNIEnv *, jobject)函数。

#include "stdafx.h"#include "JniMain.h"
#include <string.h>JNIEXPORT void JNICALL Java_JniMain_accessStaticFieldModify
(JNIEnv * env, jobject jobj){//得到jclass,即JniMain.classjclass jclz = (*env)->GetObjectClass(env,jobj);//得FieldId, "count" 属性名称,"I"属性签名jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");//获取count的值jint count = (*env)->GetStaticIntField(env,jclz,fid);count += 5;//给jobj的count静态成员变量设置新的值(*env)->SetStaticIntField(env, jclz, fid, count);}

最后编译生成.dll动态库,并把动态库放到java工程里面。
接下来加载动态库并调用,代码如下

public class JniMain {public static int count = 2;public native void accessStaticFieldModify();static{System.loadLibrary("JNI_Demo1");}public static void main(String[] args) {//调用非静态native方法JniMain jm = new JniMain();System.out.println("change before count: " + count);jm.accessStaticFieldModify();System.out.println("after change count: " + count);}}

最后的运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50813.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32——I2C和SPI波形分析

波形分析 I2C波形 //写命令 void OLED_WR_CMD(uint8_t cmd) { HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100); } //写数据 void OLED_WR_DATA(uint8_t data) { HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x40,I2C_MEMADD_SIZE_8BIT,&am…

第三届IEEE云计算、大数据应用与软件工程国际学术会议 (IEEE-CBASE 2024,10月11-13)

第三届IEEE云计算、大数据应用与软件工程国际学术会议 ( CBASE 2024 &#xff09;将于2024年10月11—13日在中国杭州举办。 该会议在连续两届成功举办的基础上&#xff0c;本届将由浙江水利水电学院、浙江省自动化学会、浙江省科协智能制造学会联合体主办&#xff0c;浙江水利水…

【轻松拿捏】设计模式六大基本原则(一)单一职责原则(SRP - Single Responsibility Principle)

&#x1f388;边走、边悟&#x1f388;迟早会好 一. 概述 单一职责原则&#xff08;SRP - Single Responsibility Principle&#xff09;是面向对象设计中的一个基本原则。它的核心思想是&#xff1a;一个类只应有一个引起它变化的原因&#xff0c;也就是说&#xff0c;一个类…

git clone报错unable to access

网页能够访问github.com和外网&#xff0c;git 也是安装了最新版&#xff0c;但是在使用 git clone xxx 时就报错&#xff1a; $ git clone https://github.com/XXX.git Cloning into XXX... fatal: unable to access https://github.com/XXXc.git/: OpenSSL SSL_connect: SSL…

C ++初阶:C++入门级知识点

目录 &#x1f31e;0.前言 &#x1f688;1.C输入输出 &#x1f688;2.缺省参数 &#x1f69d;2.1全缺省参数 &#x1f69d;2.2半缺省参数 &#x1f688;3.函数重载 &#x1f69d;3.1参数类型不同 &#x1f69d; 3.2参数个数不同 &#x1f69d;3.3参数类型顺序不同 ​…

相似度计算方法-编辑距离 (Edit Distance)

定义 编辑距离&#xff08;Edit Distance&#xff09;&#xff0c;也称为Levenshtein距离&#xff0c;是一种衡量两个字符串相似度的方法。它定义为从一个字符串转换为另一个字符串所需的最少单字符编辑操作次数&#xff0c;这些操作包括插入、删除或替换一个字符。 计算方法 …

Mysql(三)---增删查改(基础)

文章目录 前言1.补充1.修改表名1.2.修改列名1.3.修改列类型1.4.增加新列1.5.删除指定列 2.CRUD3.新增(Create)3.1.单行插入3.2.指定列插入3.3.多行插入 4.数据库的约束4.1.约束的分类4.2.NULL约束4.3.Unique约束4.4.Default 默认值约束4.5.PRIMARY KEY&#xff1a;主键约束4.6.…

谷哥剪映助手实操,批量自动化制作左右分屏视频

我给大家介绍如何用谷哥剪映助手&#xff0c;配合剪映批量制作左右分屏或上下分屏视频。 首先我们准备好剪映参考草稿&#xff0c;草稿里有主轨和复轨两条素材。一般情况下&#xff0c;副轨比主轨时长更长。剪映助手将根据主轨时长裁切副轨。 这里需要注意的是&#xff0c;在批…

十五年以来 — 战略性云平台服务的演进路径之全面呈现(含亚马逊、微软和谷歌)

Gartner每年都发布对全球IaaS平台进行评估的魔力象限报告。2023年底&#xff0c;Gartner将此项评估的名称改为“战略性云平台服务”&#xff08;Strategic cloud platform services&#xff09;&#xff0c;尽管其核心仍为IaaS&#xff0c;但是&#xff0c;毫无疑问&#xff0c…

90. UE5 RPG 实现技能的装配

在上一篇里&#xff0c;我们实现了在技能面板&#xff0c;点击技能能够显示出技能的相关描述以及下一级的技能的对应描述。 在这一篇里&#xff0c;我们实现一下技能的装配。 在之前&#xff0c;我们实现了点击按钮时&#xff0c;在技能面板控制器里存储了当前选中的技能的相关…

ZooKeeper工作原理

1. ZooKeeper工作原理 1.1 ZooKeeper角色 领导者&#xff08;Leader&#xff09;&#xff1a;在Zookeeper集群中&#xff0c;Leader是负责管理集群事务的节点。它负责处理所有的写请求&#xff0c;并将这些请求转化为事务&#xff0c;并提交事务日志。Leader节点还负责发起和决…

用C#写一个随机音乐播放器

form1中namespce里的代码如下 public partial class Form1 : Form {public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){string folder textBox1.Text;string folderPath folder; // 指定音频文件所在的文件夹路径OpenRandomFi…

vue项目上线打包后出现的问题

1、出现空白页 1.1 打包路径&#xff1a; module.exports {publicPath:./, //修改为绝对路径 } 修改完打包路径后build可以展示页面 1.2 路由模式&#xff1a; 项目上线要求是history模式&#xff0c;需要后端做重定向 前端自测可以使用h…

JavaWeb笔记_FilterListener

一.过滤器 1.1 过滤器概述 过滤器主要用来拦截目标资源&#xff08;静态资源或动态资源&#xff09;的请求和响应 &#xff08;类似地铁的安检&#xff09; 我们访问动态或静态资源都要通过URL访问&#xff1a;http://localhost:8080/... 所以过滤器本质上拦截的是URL 1.2 过滤…

dps或者ppt文件判断是否加密

doc文件是否加密可以通过fib来判断&#xff0c;例如 同样的方法判断ppt也可以&#xff0c;但是在判断wps保存的dps文件时&#xff0c;提示没有加密&#xff0c;文件双击打开时又需要密码&#xff0c;查看ppt格式文档有下面发现 查看文件的二进制发现了加密标识 后面再研究doc x…

OpenCV图像滤波(4)构建图像金字塔函数buildPyramid()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在计算机视觉和图像处理中&#xff0c;构建图像金字塔&#xff08;Image Pyramid&#xff09;是一种常用的技术&#xff0c;它生成一系列分辨率逐…

CISAW信息安全保障人员认证是否值得学习?

CISAW信息安全保障人员认证的学习难度因人而异。 如果考生具备足够的学习能力以及丰富的信息安全工作经验&#xff0c;那么考试的难度可能会相对较低。 相反&#xff0c;如果考生缺少这些条件&#xff0c;学习难度可能会相对较高。 1. 从考试内容来看&#xff0c;CISAW以概念…

Neutralinojs教程项目实战初体验(踩坑指南),干翻 electron

Neutralinojs 项目实战初体验&#xff08;踩坑指南&#xff09;&#xff0c;干翻 electron Neutralinojs 官方文档 卧槽卧槽&#xff0c;&#xff01;这个年轻人居然用浏览器把电脑关机了_哔哩哔哩_bilibili正是在下 本教程搭建的是纯原生项目&#xff0c;没有和其它前端框架…

简单快捷!Yarn的安装与使用指南

Yarn 是由 Facebook (现 Meta) 开发的包管理工具。 今天&#xff0c;我将介绍如何使用 Yarn。 目录 Yarn 的官方网站 关于安装 版本确认 开始一个新项目&#xff08;创建 package.json 文件&#xff09; 安装软件包 升级包 运行脚本 执行包的命令 卸载包 总结 Yarn 的…

低代码平台在采购管理中的革新与应用

引言 随着企业数字化转型的不断推进&#xff0c;传统的企业软件开发模式面临着诸多挑战。开发周期长、成本高、需求变更频繁等问题使得企业在快速变化的市场中难以保持敏捷性。低代码平台作为一种新的开发模式&#xff0c;凭借其“低代码”甚至“零代码”的特性&#xff0c;极大…