JNI编程之如何传递参数和返回值
- 格式:doc
- 大小:36.50 KB
- 文档页数:4
jni传类指针-回复JNI(Java Native Interface)是Java语言与本地(C或C++)代码进行交互的一种机制。
它提供了在Java虚拟机(JVM)中调用C或C++代码的能力,同时也能让本地代码调用Java代码。
在JNI中,通过使用类指针(Class Pointers)来对Java类进行引用。
本文将介绍有关JNI中类指针的知识。
在JNI中,类指针是一种特殊类型的指针,它指向Java中的类对象。
在C 或C++代码中,我们可以通过JNI函数`GetObjectClass`来获取一个类的指针。
比如下面这段代码:cppJNIEnv *env; JNI环境指针jobject obj; Java对象jclass cls = env->GetObjectClass(obj);上述代码中,`GetObjectClass`函数返回一个`jclass`类型的指针,指向`obj`对象所属的类。
一旦我们拥有了一个类的指针,我们就可以使用其他JNI函数来操作这个类。
例如,我们可以使用`GetMethodID`函数来获取类的方法ID,然后使用`CallVoidMethod`函数来调用这个方法。
下面是一个简单的示例:cppjmethodID methodId = env->GetMethodID(cls, "methodName", "()V");env->CallVoidMethod(obj, methodId);上述代码中,`GetMethodID`函数用于获取`cls`类中名为`methodName`的无参数、无返回值的方法的方法ID。
然后,`CallVoidMethod`函数用于调用该方法。
除了获取方法ID和调用方法,类指针还可以用于获取和设置类的字段。
我们可以使用`GetFieldID`函数获取字段ID,然后使用`Get<Type>Field`和`Set<Type>Field`函数来读取和修改字段的值。
C语言教程十一、函数参数的传递和值返回前面我们说的都是无参数无返回值的函数,实际程序中,我们经常使用到带参数有返回值的函数。
一、函数参数传递1.形式参数和实际参数函数的调用值把一些表达式作为参数传递给函数。
函数定义中的参数是形式参数,函数的调用者提供给函数的参数叫实际参数。
在函数调用之前,实际参数的值将被拷贝到这些形式参数中。
2.参数传递先看一个例子:void a(int); /*注意函数声明的形式*/main(){int num;scanf(%d,&num);a(num); /*注意调用形式*/}void a(int num_back) /*注意定义形式*/{printf(%d\n,num_back);}在主函数中,先定义一个变量,然后输入一个值,在a()这个函数中输出。
当程序运行a(num);这一步时,把num的值赋值给num_back,在运行程序过程中,把实际参数的值传给形式参数,这就是函数参数的传递。
形参和实参可能不只一个,如果多于一个时,函数声明、调用、定义的形式都要一一对应,不仅个数要对应,参数的数据类型也要对应。
void a(int,float);main(){int num1;float num2;scanf(%d,&num1);scanf(%f,&num2);a(num1,num2);}void a(int num1_back,float num2_back){printf(%d,%f\n,num1_back,num2_back);}上面的例子中,函数有两个参数,一个是整型,一个是浮点型,那么在声明、调用、定义的时候,不仅个数要一样,类型也要对应。
如果不对应,有可能使的编译错误,即使没错误,也有可能让数据传递过程中出现错误。
再看一个例子:void a(int);main(){int num;scanf(%d,&num);a(num);}void a(int num){printf(%d\n,num);}看上面的例子,形式参数和实际参数的标识符都是num,程序把实际参数num 的值传递给形式参数num。
jni 结构体参数(实用版)目录1.JNI 简介2.JNI 结构体参数概述3.JNI 结构体参数详细说明4.JNI 结构体参数的应用示例5.总结正文1.JNI 简介JNI,即 Java Native Interface,是 Java 与本地代码(如 C、C++)交互的桥梁。
通过 JNI,Java 程序可以调用本地代码实现的功能,本地代码也可以调用 Java 程序实现的功能。
这使得 Java 程序可以充分利用本地代码的性能优势,同时也可以方便地实现跨平台开发。
2.JNI 结构体参数概述JNI 结构体参数主要包括两个方面:函数指针和数据类型。
函数指针用于表示本地函数,数据类型用于表示本地函数的参数和返回值类型。
通过这些结构体参数,JNI 可以确保 Java 程序与本地代码之间的数据传递和函数调用正确无误。
3.JNI 结构体参数详细说明(1)函数指针JNI 中的函数指针主要有以下几种:- void* (*func)():表示一个返回值为 void 类型的函数指针。
- void* (*func)(int, int):表示一个返回值为 void 类型的函数指针,该函数有两个 int 类型的参数。
- int (*func)():表示一个返回值为 int 类型的函数指针。
- int (*func)(int):表示一个返回值为 int 类型的函数指针,该函数有一个 int 类型的参数。
(2)数据类型JNI 中的数据类型主要有以下几种:- jobject:表示 Java 对象。
- jbyte:表示 1 个字节。
- jshort:表示 2 个字节。
- jint:表示 4 个字节(即 int 类型)。
- jlong:表示 8 个字节(即 long 类型)。
- jfloat:表示 4 个字节(即 float 类型)。
- jdouble:表示 8 个字节(即 double 类型)。
- jboolean:表示布尔值。
- jmethodID:表示 Java 方法的唯一标识符。
JNI详解javanativeinterface,(jni)是一个标准的javaapi,它支持将java代码与使用其他编程语言编写的代码相集成。
在这里主要就是java和c++的交互。
1:java调用c++:首先,对java文件中要调用的方法进行本地声明,关键字为native。
它只需要宣布,而不是实施。
像publicnativevoidloginsuccessnative(stringp_qqid);publicnativestaticvoidsetstat enative(inti);然后我们需要在C++文件中实现这些方法。
这些方法有特定的格式。
我们可以使用javah命令来帮助生成这些方法的声明,调用javac来编译我们的Java类,得到类文件,然后javahyourclassname可以得到一个H文档:#include#ifdef_cplusplus}#endif#endif事实上,只要我们知道这种格式,我们也可以手动编写。
因为Java直接在CPP中查找方法,所以H文件,我们也不需要声明。
最后我们需要将c++文件,编译成so然后加入java工程,并在java中导入。
静态{system.loadlibrary(“test”);}//系统将自行判断后缀。
2:在c++中调用java方法。
在C++中调用java是比较麻烦的,因为我们需要在C++中获得java的运行环境,并找到我们想要使用的类和方法。
首先,我们需要理解几个概念:javavm:这个代表java的虚拟机。
所有的工作都是从获取虚拟机的接口开始的,如何获取这个接口呢?我们之前导入c的组件时调用了:system.loadlibrary(“test”);调用此方法时,Java将调用组件first_Onload()函数的JNI,该函数用于两个目的:一是:告诉javavm此c组件使用那一个jni版本。
如果你没有提供jni_onload()函数,vm会默认使用最老的jni1.1版本。
c语言函数调用时参数传递方式的有哪几种,分别简述他们的传
递方式
C语言函数调用时参数的传递方式主要有以下几种:
1. 值传递:函数调用时,将实际参数的值复制给形式参数,函数内部对形式参数进行修改不会影响实际参数的值。
这是最常见的参数传递方式。
2. 引用传递:通过传递变量的指针作为参数,函数内部可以直接通过指针访问和修改实际参数的值。
这种方式可以实现在函数内部改变实参的值。
3. 地址传递:传递变量的地址作为参数,在函数内部通过指针来访问和修改实际参数的值。
和引用传递类似,通过地址传递也可以改变实参的值。
4. 数组传递:将数组的首地址作为参数传递给函数,函数内部可以通过指针来访问和修改数组的元素。
5. 结构体传递:将整个结构体作为参数传递给函数,在函数内部可以直接访问和修改结构体中的成员。
需要注意的是,C语言中的参数传递都是按值传递的,包括引
用传递和地址传递。
所谓按值传递,是指在函数调用时将实参的值复制给形参,函数内部对形参的操作不会影响到实参的值。
但是通过引用传递和地址传递,可以通过指针来访问和修改实参的值,使得函数可以改变实参的值。
C多线程函数如何传参数和返回值在C多线程编程中,可以通过以下几种方式来传递参数和获取返回值:1.传递参数:-通过结构体:可以使用一个结构体来封装所有需要传递的参数,并将该结构体作为线程的参数传递。
在线程函数中,可以通过强制类型转换将参数还原为原始类型,并使用其中的成员变量。
-通过指针:可以将需要传递的参数作为指针进行传递,线程函数在收到指针后,可以通过解引用来获得参数值,或者使用指针指向的数据。
-通过全局变量:可以将参数设置为全局变量,在线程函数中直接使用该全局变量进行操作。
需要注意的是,多个线程同时修改全局变量时可能会发生竞争条件,需要使用互斥锁来保护。
2.获取返回值:-通过指针传递返回值:可以将需要返回的值设置为指针参数,线程函数在执行完毕后将结果写入该指针指向的内存位置。
主线程可以通过读取该内存位置来获取返回值。
-通过全局变量:可以将返回值设置为全局变量,在线程函数执行完毕后,在全局变量中存储结果。
主线程可以直接读取该全局变量来获取返回值。
但是同样需要注意并发操作时的竞争条件问题。
- 使用线程函数返回值:线程函数本身是可以返回一个值的,这个返回值可以通过pthread_join函数来获取。
主线程可以通过调用pthread_join函数来等待子线程执行完毕,并获取线程函数的返回值。
需要注意的是,在C多线程编程中,传递参数和获取返回值都需要考虑数据的一致性和并发性,尤其是多个线程同时对数据进行修改时可能会导致的问题。
可以使用互斥锁来保护对共享数据的访问,或者使用其他的线程同步机制来协调线程之间的执行顺序,并保证数据的一致性。
综上所述,C多线程函数可以通过结构体、指针、全局变量等方式来传递参数和获取返回值。
通过合理的设计和使用线程同步机制,可以确保线程之间的安全操作,并实现多线程程序的正确执行。
序这四种情况下你会用到本书:1、在Java程序中复用以前写过的C/C++代码。
2、自己实现一个java虚拟机3、学习不同语言如何进行协作,尤其是如何实现垃圾回收和多线程。
4、把一个虚拟机实现整合到用C/C++写的程序中。
本书是写给开发者的。
JNI在1997年第一次发布,本书总结了SUN工程师和大量开发者两年来积累的经验。
本书介绍了JNI的设计思想,对这种思想的理解是使用JNI的各种特性的基础。
本书有一部分是JA VA2平台上面的JNI特征的规范说明。
JNI程序员可以把这部分用作一个手册。
JVM开发者在实现虚拟机的时候必须遵守这些规范。
JNI的部分设计思想来源于Netscape的Java Runtime Interface(JRI)。
第一章简介JNI是JA V A平台的一个重要特征,使用它我们可以重用以前用C/C++写的大量代码。
本书既是一个编程指南也是一个JNI手册。
本书共包括三部分:1、第二章通过一个简单的例子介绍了JNI。
它的对象是对JNI不熟悉的初学者。
2、3~10章对JNI的特征进行了系统的介绍。
我们会举大量的例子来说明JNI的各个特征,这些特征都是JNI中重要且常用的。
3、11~13章是关于JNI的技术规范。
可以把这两章当作一个手册。
本书尽量去满足各类读者的需要。
指南面向初学者,手册面向有经验的人和自己实现JNI 规范的人。
大部分读者可能是用JNI来写程序的开发者。
本书会假设你有JA V A,C/C++基础。
本章的剩余部分介绍了JNI的背景,扮演的角色和JNI的演化。
1.1 JA V A平台和系统环境(Host Environment)系统环境代指本地操作系统环境,它有自己的本地库和CPU指令集。
本地程序(Native Applications)使用C/C++这样的本地语言来编写,被编译成只能在本地系统环境下运行的二进制代码,并和本地库链接在一起。
本地程序和本地库一般地会依赖于一个特定的本地系统环境。
Androidjnindk编程⼆:jni数据类型转换(primitive,String,a。
⼀.数据类型映射概述从我们开始jni编程起,就不可能避开函数的参数与返回值的问题。
java语⾔的数据类型和c/c++有很多不同的地⽅,所以我们必须考虑当在java层调⽤c/c++函数时,怎么正确的把java的参数传给c/c++函数,怎么正确的从c/c++函数获取正确的函数返回值;反之,当我们在c/c++中使⽤java的⽅法或属性时,如何确保数据类型能正确的在java和c/c++之间转换。
回顾我们上⼀篇⽂章中的那个c函数:#include <stdio.h>#include <jni.h>#include <stdlib.h>JNIEXPORT jstring JNICALL Java_com_jinwei_jnitesthello_MainActivity_sayHello(JNIEnv * env, jobject obj){return (*env)->NewStringUTF(env,"jni say hello to you");}这个函数⾮常简单,它没有接受参数,但是它返回了⼀个字符串给java层。
我们不能简单的使⽤return “jni say hello to you”;⽽是使⽤了NewStringUTF函数做了个转换,这就是数据类型的映射。
普通的jni函数⼀般都会有两个参数:JNIEnv * env, jobject obj,第三个参数起才是该函数要接受的参数,所以这⾥说它没有接受参数。
1.1JNIEnv * envJNIEnv是⼀个线程相关的结构体, 该结构体代表了 Java 在本线程的运⾏环境。
这意味不同的线程各⾃拥有各⼀个JNIEnv结构体,且彼此之间互相独⽴,互不⼲扰。
NIEnv结构包括了JNI函数表,这个函数表中存放了⼤量的函数指针,每⼀个函数指针⼜指向了具体的函数实现,⽐如,例⼦中的NewStringUTF函数就是这个函数表中的⼀员。
jni 参数返回值JNI (Java Native Interface) 是 Java 平台标准的一部分,它允许 Java 代码与其他语言编写的代码进行交互。
JNI 允许 Java 代码调用本地方法,这些本地方法通常是用 C、C++ 或其他语言编写的。
当在 JNI 中定义本地方法时,你需要考虑参数和返回值的类型。
下面是一些常见的 JNI 参数和返回值类型:1. 基本数据类型:如 `int`, `char`, `boolean`, `float`, `double` 等。
2. 引用类型:如 `String`, `Object` 等。
3. 数组:可以是基本数据类型数组或对象数组。
4. 结构体:如 `jobject`, `jclass`, `jarray` 等。
返回值类型与参数类型类似,也可以是上述任何类型。
下面是一个 JNI 方法定义的示例,该方法接受一个字符串参数并返回一个字符串:```cJNIEXPORT jstring JNICALL Java_MyClass_myMethod(JNIEnv env, jobject obj, jstring param) {const char nativeParam = (env)->GetStringUTFChars(env, param, NULL);// 在这里执行一些操作,例如将 nativeParam 转换为其他字符串jstring returnValue = (env)->NewStringUTF(env, "Some string"); // 释放本地字符串资源(env)->ReleaseStringUTFChars(env, param, nativeParam);return returnValue;}```在这个示例中,`Java_MyClass_myMethod` 是本地方法的名称,它遵循特定的命名规则:`Java_` + 包名 + 类名 + 方法名。
jni callstaticintmethod 原理
JNI (Java Native Interface) 是一个支持Java程序和本地代码进行交互的桥梁。
通过JNI,Java程序可以调用本地代码中定义的方法,反之亦然。
在JNI中,CallStaticIntMethod是一种调用本地代码中的静态方法并返回int值的方法。
在调用CallStaticIntMethod时,需要使用JavaVM来获取JNIEnv环境。
JNIEnv是一个代表本地方法接口的结构体,通过它可以调用本地方法。
调用CallStaticIntMethod方法的原理如下:
1. 通过JavaVM获取JNIEnv环境,JNIEnv是Java程序与本地代码进行交互的接口。
2. 使用JNIEnv的CallStaticIntMethod函数调用指定类的静态方法。
需要传入以下参数:
- 指定类的引用:通过FindClass方法获取指定类的引用。
- 静态方法的引用:通过GetStaticMethodID方法获取静态方法的引用。
- 方法参数:如果方法需要参数,需要将参数转换为对应的本地类型,并作为CallStaticIntMethod的参数传入。
3. 返回方法执行结果。
总结起来,CallStaticIntMethod方法实际上是通过JNI提供的接口,调用本地代码中定义的指定类的静态方法,并将其返回值作为int返回给Java程序。
jni 结构体传递在JNI(Java Native Interface)中,可以使用结构体(struct)作为参数进行传递。
要传递结构体参数,首先需要定义相应的结构体类型,然后按照JNI的规则使用JNIEnv的函数来访问结构体的成员。
以下是一个示例代码,演示了如何在JNI中传递结构体参数:1. 首先,在C中定义结构体类型,例如:```ctypedef struct {int intValue;float floatValue;} MyStruct;```2. 在JNI中,声明一个与上述结构体对应的Java类,并创建与结构体一致的属性和方法。
例如,在Java中,定义一个名为MyStruct的类,包含一个名为intValue的整型属性和一个名为floatValue的浮点型属性。
3. 在JNI函数中,可以使用`GetFieldID`和`SetIntField`等JNIEnv的函数来获取和修改结构体的属性值。
例如:```cJNIEXPORT void JNICALLJava_com_example_example_MyClass_changeStruct(JNIEnv*env, jobject obj, jobject myStructObj) {jclass myStructClass = (*env)->GetObjectClass(env, myStructObj);jfieldID intValueFieldId = (*env)->GetFieldID(env, myStructClass, "intValue", "I");jfieldID floatValueFieldId = (*env)->GetFieldID(env, myStructClass, "floatValue", "F");jint intValue = (*env)->GetIntField(env, myStructObj, intValueFieldId);jfloat floatValue = (*env)->GetFloatField(env, myStructObj, floatValueFieldId);// 修改结构体的属性值intValue = 10;floatValue = 3.14;(*env)->SetIntField(env, myStructObj, intValueFieldId, intValue);(*env)->SetFloatField(env, myStructObj, floatValueFieldId, floatValue);}```在Java代码中,可以调用JNI函数并将结构体对象作为参数传递,JNI函数将修改结构体对象的值。
调用函数时的三种参数传递方式(传值传引用传地址)在编程语言中,参数是在函数调用过程中传递给函数的值或变量。
参数传递方式可以分为传值、传引用和传地址三种。
1. 传值(Call by Value):参数按照值的方式进行传递,函数接收的是实际参数的一个副本。
在函数内部对参数的修改不会影响到原始的参数。
这是最常见的参数传递方式,在许多编程语言中都是默认的方式。
特点:-参数的值在函数内部是只读的,函数不会对原始的参数进行修改。
-通过副本传递参数,避免了对原始参数进行意外修改的风险。
优点:-参数的值是独立于函数之外的,可以保证函数的安全性和一致性。
-函数内部对参数的修改不会影响到原始的参数,避免了意外的副作用。
缺点:-对于较大的数据类型,由于需要复制参数的值,会消耗更多的内存和时间。
2. 传引用(Call by Reference):参数按引用的方式进行传递,函数接收的是实际参数的引用或指针。
在函数内部对参数的修改会影响到原始的参数。
在引用传递参数的语言中,使用引用的方式可以让函数修改原始参数的值。
特点:-参数在函数内部是可写的,可以对参数进行修改。
-函数通过引用访问参数,可以直接修改原始参数的值。
优点:-函数可以直接修改原始参数的值,方便了对参数的修改操作。
-不需要复制参数的值,减少了内存和时间的消耗。
缺点:-参数的值可以被函数随时修改,可能导致意外的副作用,使程序变得难以理解和调试。
-对于没有被传引用的参数,无法从函数内部访问到其值。
3. 传地址(Call by Address):参数按照地址的方式进行传递,函数接收的是实际参数的地址。
在函数内部对参数进行修改会影响到原始的参数。
传地址方式类似于传引用,不同之处在于传地址是通过参数的指针来修改参数的值。
特点:-参数在函数内部是可写的,可以对参数进行修改。
-函数使用指针访问参数,可以直接修改原始参数的值。
优点:-函数可以直接修改原始参数的值,方便了对参数的修改操作。
jni回调函数
JNI(Java Native Interface)回调函数是在JNI中用于实现Java 和C或C++程序之间交互的一种机制。
通常情况下,Java应用程序可以通过JNI调用C或C++函数,这些函数可以执行一些底层的操作,比如访问操作系统API或硬件设备等。
而回调函数则是C或C++代码向Java代码传递消息的一种方式。
JNI回调函数的实现步骤如下:
1.在C或C++代码中实现回调函数,并使用JNI注册该函数。
2.在Java代码中定义一个接口,并实现该接口。
该接口定义回调函数的格式和语义。
3.在Java代码中创建一个对象并将其传递给C或C++代码作为回调函数的参数。
4.在C或C++代码中调用Java代码中定义的回调函数,传递相应的参数。
5.在Java代码中处理从C或C++代码传递过来的参数。
在实际的应用中,JNI回调函数常常用于实现Java应用程序与底层硬件设备的通信,比如音频、视频、图像等的处理。
jna 结构体二级指针传参全文共四篇示例,供读者参考第一篇示例:JNA(Java Native Access)是一种Java程序与本地代码(如C、C++)进行交互的工具。
它通过JNI(Java Native Interface)技术,允许Java程序调用本地代码中的函数,实现Java与本地代码的无缝连接。
在使用JNA时,我们可能会遇到需要传递结构体二级指针的情况。
本文将详细介绍如何在JNA中传递结构体二级指针。
在C语言中,结构体二级指针通常用来传递指向结构体指针的指针。
在JNA中,结构体二级指针的传递涉及到如何正确地定义结构体、结构体指针和结构体二级指针,并在Java代码中进行正确的传参和取值操作。
我们需要在Java中定义需要传递的结构体。
在JNA中,我们可以使用JNA库提供的`Structure`类来定义结构体。
我们定义一个表示学生信息的结构体`Student`如下:```javapublic class Student extends Structure {public int id;public String name;}```接着,我们需要定义结构体指针和结构体二级指针。
在JNA中,结构体指针对应C语言中的指针类型`Pointer`,结构体二级指针对应C 语言中的指向指针的指针类型`PointerByReference`。
我们可以按照以下方式定义结构体指针和结构体二级指针:```javaStudent.ByReference studentRef = newStudent.ByReference();Student[] studentArray = (Student[]) studentRef.toArray(1);PointerByReference studentPtrRef = new PointerByReference();studentPtrRef.setValue(studentArray[0].getPointer());```接着,我们就可以在JNA中进行结构体二级指针的传参了。
jni callstaticvoidmethod 参数说明全文共四篇示例,供读者参考第一篇示例:JNI(Java Native Interface)是Java提供给开发者的一种机制,允许Java程序与本地(native)程序(如C或C++程序)进行交互。
在JNI中,有一个重要的概念,即调用本地方法,也就是通过JNI调用本地程序的方法。
在JNI中,我们可以通过CallStaticVoidMethod方法来调用本地方法,并传递参数给本地方法。
在这篇文章中,我们将详细介绍JNI中调用CallStaticVoidMethod方法时传递参数的相关内容。
让我们来了解一下CallStaticVoidMethod方法的定义:```javavoid CallStaticVoidMethod(JNIEnv *env, jclass clazz, jmethodID methodID, ...);```从上面的定义可以看出,CallStaticVoidMethod方法接受四个参数:JNIEnv *env,jclass clazz,jmethodID methodID和可变参数列表。
在这里,我们只关注传递参数的部分,也就是可变参数列表。
1. 参数个数和类型:在传递参数时,我们需要根据本地方法的定义确定参数的个数和类型,确保传递的参数与本地方法的参数一一对应。
如果参数类型不匹配,可能会导致程序崩溃或出现其他异常。
2. 参数顺序:传递参数的顺序也非常重要,需要按照本地方法定义的参数顺序来传递参数,否则可能导致参数值被错误地传递给本地方法。
3. 参数值的获取:在JNI中,参数的值是以不同的数据类型进行传递的,我们需要使用相关的JNI函数(如GetIntField、GetFloatField等)来获取参数的值,并将其转换为本地方法所需的数据类型。
以上是关于在JNI中调用CallStaticVoidMethod方法时传递参数的一些注意事项,通过合理的传递参数,我们可以有效地与本地程序进行交互,实现更加复杂和功能强大的程序功能。
jni 调用native code 申请的内存释放-回复JNI(Java Native Interface)是一种允许Java代码与本机(native)代码(C、C++等)进行交互的机制。
它提供了一个桥梁,使Java程序能够调用本地库中的函数,并传递参数和返回值。
在使用JNI时,可能会涉及到申请内存并在本地代码中使用和释放内存的操作。
这篇文章将逐步解释如何在JNI调用本机代码时申请和释放内存。
第一步:JNI中的内存管理在JNI中,可以通过以下几种方式来申请和释放内存:1. 使用JNIEnv的NewObjectArray()和NewByteArray()等方法来创建数组和对象,并使用DeleteLocalRef()方法释放这些本地引用;2. 使用NewGlobalRef()和DeleteGlobalRef()方法创建和释放全局引用;3. 使用NewDirectByteBuffer()和GetDirectBufferAddress()方法来创建和释放直接缓冲区。
第二步:JNI中申请内存的方法和函数在JNI中,我们可以使用以下几种方式来申请内存:1. 使用NewByteArray()方法创建一个字节数组,并将其保存在一个jbyteArray对象中。
例如:jbyteArray byteArray = env->NewByteArray(len);2. 使用NewIntArray()方法创建一个整型数组,并将其保存在一个jintArray对象中。
例如:jintArray intArray = env->NewIntArray(len);3. 使用NewObjectArray()方法创建一个对象数组,并将其保存在一个jobjectArray对象中。
例如:jobjectArray objArray = env->NewObjectArray(len, className, NULL);第三步:JNI中释放内存的方法和函数在JNI中,我们可以使用以下几种方式来释放内存:1. 使用DeleteLocalRef()方法释放本地引用。
JNI中基本类型数组的传递方法(无需拷贝数据!!!)0、先来看一下主要用到哪些函数:C 代码1. GetIntArrayElements();//貌似得到的是副本,要拷贝数据2. ReleaseIntArrayElements();//对应上面的函数的释放资源的函数3. env->GetPrimitiveArrayCritical();//貌似得到的是指向原数据的指针4. env->ReleasePrimitiveArrayCritical();////对应上面的函数的释放资源的函数官方文档:/javase/7/docs/technotes/guides/jni/spec/functions.html#wp17440JNI函数的中译本(貌似没看到GetPrimitiveArrayCritical()):/qinjuning下面正式开始:1、不知道如何设置JNI环境的先看这里:/blog/13281362、Java端程序:Java端:Java代码1. package tests;2.3. import java.util.Arrays;4.5. public class TestJNIArray {6. static{7. System.loadLibrary("TestJNIArray");8. }9.10. public static native void addOne(int[] ints);//数组元素 111. public static native void addOne(double[] ints);//数组元素 1,为了测试,C 中循环了5次12. public static native int[] getNewArray(int size,int initValue);//生成初始值为initValue的数组,数组长度为size13.14. public static void main(String[] args) throws InterruptedException {15. int n=20;16.17. final int[] a=new int[n];18.19. for (int i = 0; i < a.length; i ) {20. a[i]=i;21. }22. if(n<50)System.out.println(Arrays.toString(a));23. addOne(a);24. if(n<50)System.out.println(Arrays.toString(a));25.26.27. final double d[]=new double[n];28. for (int i = 0; i < d.length; i ) {29. d[i]=i;30. }31. //addOne(d);32. if(n<50)System.out.println(Arrays.toString(d));33.34.35. new Thread(new Runnable() {36.37. @Override38. public void run() {39. addOne(d);40.41. }42. }).start();43. for (int i = 0; i < 200; i ) {44. Thread.sleep(20);45. System.out.println(Arrays.toString(d));46. if(d[d.length-1]-d[0]!=n-1)System.out.println("检测到C 端更新数据中");//看看能否找到在C 更新数组时Java端又读取数据的情况47. }48.49.50. int[] b=getNewArray(2, 9);51. System.out.println(Arrays.toString(b));52.53.54. int[] c=getNewArray(0, 9);55. System.out.println(Arrays.toString(c));56.57. }58. }3、C 端程序:C 代码1. #include "tests_TestJNIArray.h"2. #include <windows.h>3.4. #include <time.h>5. #include <iostream>6. #include <string>7.8. class Timer{9. private:10. clock_t time;11. public:12. Timer(){13. time=clock();14. }15.16. clock_t getElapsedTime(){17. return clock()-time;18. }19.20. clock_t getElapsedTimeAndRestart(){21. clock_t tmp=time;22. time=clock();23. return time-tmp;24. }25.26. void restart(){27. time=clock();28. }29.30. int getCLOCKS_PER_SEC(){31. return CLOCKS_PER_SEC;32. }33. };34.35. JNIEXPORT void JNICALL Java_tests_TestJNIArray_addOne___3I( JNIEnv * env, jclass, jintArray intArray) {36. jboolean b;37.38. Timer timer;39. jint* pint=env->GetIntArrayElements(intArray,&b);//获取指针!!第二个参数可为NULL40. std::string str=b?"true":"false";41. long t=timer.getElapsedTimeAndRestart();42. std::cout<<"GetIntArrayElements()耗时: "<<t<<"\t为副本?"<<str<<std::endl;//很悲剧,自己得到的是副本(copy了一份,速度慢啊)43. jsize size=env->GetArrayLength(intArray);44.45. for (int i=0; i<size; i )46. pint[i] =1;47. env->ReleaseIntArrayElements(intArray,pint,0);//释放~~~48. // 对于最后一个参数(如果指针指向的数组为副本时,否则该参数不起作用)49. // 0 copy back the content and free the elems buffer50. // JNI_COMMIT copy back the content but do not free the elems buffer51. // JNI_ABORT free the buffer without copying back the possible changes52.53. std::cout<<"从c 程序返回~"<<std::endl;54.55.56. }57.58. JNIEXPORT void JNICALL Java_tests_TestJNIArray_addOne___3D( JNIEnv * env, jclass, jdoubleArray intArray)59. {60. jboolean b;61. Timer timer;62. double* pd=(double*)env->GetPrimitiveArrayCritical(intArray,&b);63. //if(pd==NULL)return;理论上应该检查64. long t=timer.getElapsedTimeAndRestart();65. jsize size=env->GetArrayLength(intArray);66. std::string str=b?"true":"false";67. std::cout<<"GetPrimitiveArrayCritical()耗时: "<<t<<"\t为副本?"<<str<<std::endl;//这次是原始数据了,happy啊68.69. for(int j=0;j<5;j ){ //验证一下,Java中的数据也在更新70. Sleep(1000);71. for (int i=0; i<size; i ){72. pd[i] =1;73. Sleep(10);74. }75. }76. env->ReleasePrimitiveArrayCritical(intArray,pd,0);//别忘了释放~~~虽然不知道不释放有什么问题。
jni 结构体传递摘要:1.JNI 简介2.JNI 结构体的定义3.JNI 结构体传递的实现4.JNI 结构体传递的实例5.JNI 结构体传递的优缺点正文:1.JNI 简介JNI,即Java Native Interface,是Java 与本地代码(如C、C++)交互的桥梁。
通过JNI,Java 程序可以调用本地代码实现的功能,本地代码也可以调用Java 程序实现的功能。
这样,Java 程序可以充分利用本地代码的性能优势,同时也可以方便地实现跨平台。
2.JNI 结构体的定义在JNI 中,结构体是用来表示本地方法的参数和返回值的数据结构。
结构体定义的一般形式如下:```ctypedef struct {// 参数类型1// 参数类型2//...// 参数类型N} JNIEnv;```3.JNI 结构体传递的实现JNI 结构体传递的实现主要依赖于JNI 函数。
JNI 函数是Java 与本地代码交互的接口,它负责将Java 方法的参数和返回值传递给本地方法。
在实现JNI 结构体传递时,需要遵循以下步骤:(1)创建Java 类,并在Java 类中声明本地方法。
```javapublic class MyClass {static {System.loadLibrary("mylibrary");}private native int myNativeMethod(int a, int b);}```(2)在C/C++代码中实现本地方法。
```c#include <jni.h>#include "MyClass.h"JNIEXPORT jint JNICALL Java_MyClass_myNativeMethod(JNIEnv*env, jobject obj, jint a, jint b) {return a + b;}```(3)在Java 程序中调用本地方法。
首先要强调的是,native方法不但可以传递Java的基本类型做参数,还可以传递更复杂的类型,比如String,数组,甚至自定义的类。
这一切都可以在jni.h中找到答案。
1. Java基本类型的传递用过Java的人都知道,Java中的基本类型包括boolean,byte,char,short,int,long,float,double 这样几种,如果你用这几种类型做native方法的参数,当你通过javah -jni 生成.h文件的时候,只要看一下生成的.h文件,就会一清二楚,这些类型分别对应的类型是 jboolean,jbyte,jchar,jshort,jint,jlong,jfloat,jdouble 。
这几种类型几乎都可以当成对应的C++类型来用,所以没什么好说的。
2. String参数的传递Java的String和C++的string是不能对等起来的,所以处理起来比较麻烦。
先看一个例子,class Prompt {// native method that prints a prompt and reads a lineprivate native String getLine(String prompt);public static void main(String args[]) {Prompt p = new Prompt();String input = p.getLine("Type a line: ");System.out.println("User typed: " + input);}static {System.loadLibrary("Prompt");}}在这个例子中,我们要实现一个native方法String getLine(String prompt);读入一个String参数,返回一个String值。
通过执行javah -jni得到的头文件是这样的#include <jni.h>#ifndef _Included_Prompt#define _Included_Prompt#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt);#ifdef __cplusplus}#endif#endifjstring是JNI中对应于String的类型,但是和基本类型不同的是,jstring不能直接当作C++的string用。
如果你用cout << prompt << endl;编译器肯定会扔给你一个错误信息的。
其实要处理jstring有很多种方式,这里只讲一种我认为最简单的方式,看下面这个例子,#include "Prompt.h"#include <iostream>JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt){const char* str;str = env->GetStringUTFChars(prompt, false);if(str == NULL) {return NULL; /* OutOfMemoryError already thrown */}std::cout << str << std::endl;env->ReleaseStringUTFChars(prompt, str);char* tmpstr = "return string succeeded";jstring rtstr = env->NewStringUTF(tmpstr);return rtstr;}在上面的例子中,作为参数的prompt不能直接被C++程序使用,先做了如下转换str = env->GetStringUTFChars(prompt, false);将jstring类型变成一个char*类型。
返回的时候,要生成一个jstring类型的对象,也必须通过如下命令,jstring rtstr = env->NewStringUTF(tmpstr);这里用到的GetStringUTFChars和NewStringUTF都是JNI提供的处理String类型的函数,还有其他的函数这里就不一一列举了。
3. 数组类型的传递和String一样,JNI为Java基本类型的数组提供了j*Array类型,比如int[]对应的就是jintArray。
来看一个传递int数组的例子,Java程序就不写了,JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr){jint *carr;carr = env->GetIntArrayElements(arr, false);if(carr == NULL) {return 0; /* exception occurred */}jint sum = 0;for(int i=0; i<10; i++) {sum += carr[i];}env->ReleaseIntArrayElements(arr, carr, 0);return sum;}这个例子中的GetIntArrayElements和ReleaseIntArrayElements函数就是JNI提供用于处理int数组的函数。
如果试图用arr[i]的方式去访问jintArray类型,毫无疑问会出错。
JNI还提供了另一对函数GetIntArrayRegion和 ReleaseIntArrayRegion访问int数组,就不介绍了,对于其他基本类型的数组,方法类似。
4. 二维数组和String数组在JNI中,二维数组和String数组都被视为object数组,因为数组和String被视为object。
仍然用一个例子来说明,这次是一个二维int数组,作为返回值。
JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass cls, int size){jobjectArray result;jclass intArrCls = env->FindClass("[I");result = env->NewObjectArray(size, intArrCls, NULL);for (int i = 0; i < size; i++) {jint tmp[256]; /* make sure it is large enough! */jintArray iarr = env->NewIntArray(size);for(int j = 0; j < size; j++) {tmp[j] = i + j;}env->SetIntArrayRegion(iarr, 0, size, tmp);env->SetObjectArrayElement(result, i, iarr);env->DeleteLocalRef(iarr);}return result;}上面代码中的第三行,jobjectArray result;因为要返回值,所以需要新建一个jobjectArray对象。
jclass intArrCls = env->FindClass("[I");是创建一个jclass的引用,因为result的元素是一维int数组的引用,所以intArrCls必须是一维int数组的引用,这一点是如何保证的呢?注意FindClass的参数"[I",JNI 就是通过它来确定引用的类型的,I表示是int类型,[标识是数组。
对于其他的类型,都有相应的表示方法,Z booleanB byteC charS shortI intJ longF floatD doubleString是通过“Ljava/lang/String;”表示的,那相应的,String数组就应该是“[Ljava/lang/String;”。
还是回到代码,result = env->NewObjectArray(size, intArrCls, NULL);的作用是为result分配空间。
jintArray iarr = env->NewIntArray(size);是为一维int数组iarr分配空间。
env->SetIntArrayRegion(iarr, 0, size, tmp);是为iarr赋值。
env->SetObjectArrayElement(result, i, iarr);是为result的第i个元素赋值。
通过上面这些步骤,我们就创建了一个二维int数组,并赋值完毕,这样就可以做为参数返回了。
如果了解了上面介绍的这些内容,基本上大部分的任务都可以对付了。
虽然在操作数组类型,尤其是二维数组和String数组的时候,比起在单独的语言中编程要麻烦,但既然我们享受了跨语言编程的好处,必然要付出一定的代价。
有一点要补充的是,本文所用到的函数调用方式都是针对C++的,如果要在C中使用,所有的env->都要被替换成(*env)->,而且后面的函数中需要增加一个参数env,具体请看一下jni.h的代码。
另外还有些省略的内容,可以参考JNI的文档:Java Native Interface 6.0 Specification,在JDK的文档里就可以找到。
如果要进行更深入的JNI编程,需要仔细阅读这个文档。