JNI基础知识:原型数据——如何传递字符串(Java编程教程第4.2节)
Java JNI程序: TestJNIString.java
public class TestJNIString {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Native method that receives a Java String and return a Java String
private native String sayHello(String msg);
public static void main(String args[]) {
String result = new TestJNIString().sayHello("Hello from Java");
System.out.println("In Java, the returned string is: " + result);
}
}
这个JNI程序声明了一个native
方法sayHello()
,它接收一个Java String
并返回一个Java String
。 main()
方法调用sayHello()
。
编译Java程序并生成C / C ++头文件“ TestJNIString.h
”:
javac -h . TestJNIString.java
C实现 - TestJNIString.c
头文件TestJNIString.h
包含此函数声明:
JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *, jobject, jstring);
JNI定义了一个jstring
类型来表示Java String
。 最后一个参数(JNI类型为jstring
)是传递给C程序的Java String
。 返回类型也是jstring
。
传递字符串比传递基元更复杂,因为Java的String
是一个对象(引用类型),而C-string是一个以NULL结尾的char
数组。 您需要在Java String
(表示为JNI jstring
)和C-string( char*
)之间进行转换。
JNI环境(通过参数JNIEnv*
访问)提供转换功能:
- 要从JNI字符串(
jstring
)获取C字符串(char*
),请调用方法const char* GetStringUTFChars(JNIEnv*, jstring, jboolean*)
。 - 要从C字符串(
char*
)获取JNI字符串(jstring
),请调用方法jstring NewStringUTF(JNIEnv*, char*)
。
C实现TestJNIString.c
如下。
- 它接收JNI字符串(
jstring
),通过GetStringUTFChars()
转换为C字符串(char*
GetStringUTFChars()
。 - 然后它执行其预期的操作 - 显示收到的字符串并提示用户返回另一个字符串。
- 它通过
NewStringUTF()
将返回的C字符串(char*
)转换为JNI字符串(jstring
NewStringUTF()
,并返回jstring
。
#include <jni.h>
#include <stdio.h>
#include "TestJNIString.h"
JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
// Step 1: Convert the JNI String (jstring) into C-String (char*)
const char *inCStr = (*env)->GetStringUTFChars(env, inJNIStr, NULL);
if (NULL == inCSt) return NULL;
// Step 2: Perform its intended operations
printf("In C, the received string is: %s\n", inCStr);
(*env)->ReleaseStringUTFChars(env, inJNIStr, inCStr); // release resources
// Prompt user for a C-string
char outCStr[128];
printf("Enter a String: ");
scanf("%s", outCStr); // not more than 127 characters
// Step 3: Convert the C-string (char*) into JNI String (jstring) and return
return (*env)->NewStringUTF(env, outCStr);
}
将C程序编译为共享库。
gcc -I"<JAVA_HOME>\include" -I"<JAVA_HOME>\include\win32" -shared -o myjni.dll TestJNIString.c
现在,运行Java程序:
java -Djava.library.path=. TestJNIString
In C, the received string is: Hello from Java
Enter a String: test
In Java, the returned string is: test
JNI本机字符串函数
JNI支持Unicode(16位字符)和UTF-8(1-3字节编码)字符串的转换。 UTF-8字符串的作用类似于以null结尾的C字符串( char
数组),应该在C / C ++程序中使用。
JNI字符串( jstring
)函数是:
// UTF-8字符串(编码为1-3字节,向后兼容7位ASCII)
//可以映射到以null结尾的char-array C-string
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
//返回一个指向字节数组的指针,该字节数组表示修改后的UTF-8编码中的字符串。
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
//通知VM本机代码不再需要访问utf。
jstring NewStringUTF(JNIEnv *env, const char *bytes);
//从修改后的UTF-8编码中的字符数组构造一个新的java.lang.String对象。
jsize GetStringUTFLength(JNIEnv *env, jstring string);
//返回字符串的修改后的UTF-8表示的字节长度。
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize length, char *buf);
//将从offset start开始的len个Unicode字符转换为修改后的UTF-8编码
//并将结果放在给定的缓冲区buf中。
// Unicode字符串(16位字符)
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
//返回指向Unicode字符数组的指针
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
//通知VM本机代码不再需要访问字符。
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize length);
//从Unicode字符数组构造一个新的java.lang.String对象。
jsize GetStringLength(JNIEnv *env, jstring string);
//返回Java字符串的长度(Unicode字符数)。
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize length, jchar *buf);
//将从offset start开始的len个Unicode字符数复制到给定的缓冲区buf
UTF-8字符串或C字符串
GetStringUTFChars()
函数可用于从给定的Java的jstring
创建新的C字符串( char*
)。 如果无法分配内存,则该函数返回NULL
。 检查NULL
是一个好习惯。
第三个参数isCopy
(of jboolean*
),它是一个“in-out”参数,如果返回的字符串是原始java.lang.String
实例的副本,则将设置为JNI_TRUE
。 如果返回的字符串是指向原始String
实例的直接指针,则它将设置为JNI_FALSE
- 在这种情况下,本机代码不应修改返回的字符串的内容。 如果可能,JNI运行时将尝试返回直接指针; 否则,它返回一份副本。 尽管如此,我们很少对修改底层字符串感兴趣,并且经常传递NULL
指针。
每当您不需要返回的GetStringUTFChars()
字符串来释放内存和引用以便可以对其进行垃圾回收时,始终调用ReleaseStringUTFChars()
。
NewStringUTF()
函数使用给定的C字符串创建一个新的JNI字符串( jstring
)。
JDK 1.2引入了GetStringUTFRegion()
,它将jstring
(或从length
start
的一部分GetStringUTFRegion()
复制到“ 预分配”的 C的char
数组中。 可以使用它们代替GetStringUTFChars()
。 由于预先分配了C的数组,因此不需要isCopy
。
JDK 1.2还引入了Get/ReleaseStringCritical()
函数。 与GetStringUTFChars()
类似,如果可能,它返回一个直接指针; 否则,它返回一份副本。 本机方法不应阻止(对于IO或其他)一对GetStringCritical()
和ReleaseStringCritical()
调用。
Unicode字符串
它使用jchar*
来存储Unicode字符,而不是char*
。
#include <jni.h>
#include <iostream>
#include <string>
#include "TestJNIString.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
// Step 1: Convert the JNI String (jstring) into C-String (char*)
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
// Step 2: Perform its intended operations
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr); // release resources
// Prompt user for a C++ string
string outCppStr;
cout << "Enter a String: ";
cin >> outCppStr;
// Step 3: Convert the C++ string to C-string, then to JNI String (jstring) and return
return env->NewStringUTF(outCppStr.c_str());
}
编译:
g++ -I"<JAVA_HOME>\include" -I"<JAVA_HOME>\include\win32" -shared -o myjni.dll TestJNIString.cpp
请注意,C ++本机字符串函数与C语法不同。在C ++中,我们可以使用“env->”而不是“(env *) - >”。 此外,C ++函数中不需要JNIEnv *参数。
另请注意,C ++支持一个字符串类(在标题<string>下面,它更加用户友好,以及传统的C字符串(char数组))。
[TODO]是否直接支持C ++字符串类?
4.3传递基元数组
JNI计划 - TestJNIPrimitiveArray.java
|
公共类TestJNIPrimitiveArray { 静态的 { 的System.loadLibrary( “myjni”); // myjni.dll(Windows)或libmyjni.so(Unixes) } //声明一个本机方法sumAndAverage()接收一个int []和 //返回一个double [2]数组,其中[0]为sum,[1]为平均值 private native double [] sumAndAverage(int [] numbers); //测试驱动程序 public static void main(String args []){ int [] numbers = {22,33,33}; double [] results = new TestJNIPrimitiveArray()。sumAndAverage(numbers); System.out.println(“在Java中,总和是”+结果[0]); System.out.println(“在Java中,平均值是”+结果[1]); } } |
C实现 - TestJNIPrimitiveArray.c
标题“ TestJNIPrimitiveArray.h
”包含以下函数声明:
<span style="color:#000000">JNIEXPORT jdoubleArray JNICALL Java_TestJNIPrimitiveArray_average(JNIEnv *,jobject,jintArray); </span>
在Java中,数组是一种引用类型,类似于类。有9种类型的Java数组,八个基元中的每一个和一个数组java.lang.Object
。 JNI定义了类型为每个八个Java原始阵列,即jintArray
,jbyteArray
,jshortArray
,jlongArray
,jfloatArray
,jdoubleArray
,jcharArray
,jbooleanArray
用于Java的原始阵列int
,byte
,short
,long
,float
,double
,char
和boolean
分别。它还定义了一个jobjectArray
for Java的数组Object
(稍后讨论)。
同样,你需要JNI阵列和本地阵列,例如之间的转换,之间jintArray
和C的jint[]
,或jdoubleArray
和C的jdouble[]
。JNI Environment接口为转换提供了一组函数:
- 要从
jint[]
JNI 获取C本机jintArray
,请调用jint* GetIntArrayElements(JNIEnv *env, jintArray a, jboolean *iscopy)
。 - 要从
jintArray
C本机获取JNIjint[]
,首先,调用jintArray NewIntArray(JNIEnv *env, jsize len)
分配,然后使用void SetIntArrayRegion(JNIEnv *env, jintArray a, jsize start, jsize len, const jint *buf)
从中复制jint[]
到jintArray
。
上面有8组函数,一组用于八个Java原语中的每一个。
本机程序需要:
- 接收传入的JNI阵列(例如
jintArray
),转换为C的本机阵列(例如jint[]
)。 - 执行其预期的操作。
- 将返回C的本机数组(例如
jdouble[]
)转换为JNI数组(例如jdoubleArray
),并返回JNI数组。
C实现“ TestJNIPrimitiveArray.c
”是:
|
#include <jni.h> #include <stdio.h> #include“TestJNIPrimitiveArray.h” JNIEXPORT jdoubleArray JNICALL Java_TestJNIPrimitiveArray_sumAndAverage (JNIEnv * env,jobject thisObj,jintArray inJNIArray){ //第1步:将传入的JNI jintarray转换为C的jint [] jint * inCArray =(* env) - > GetIntArrayElements(env,inJNIArray,NULL); if(NULL == inCArray)返回NULL; jsize length =(* env) - > GetArrayLength(env,inJNIArray); //第2步:执行预期的操作 jint sum = 0; int i; for(i = 0; i <length; i ++){ sum + = inCArray [i]; } jdouble average =(jdouble)sum / length; (* env) - > ReleaseIntArrayElements(env,inJNIArray,inCArray,0); //释放资源 jdouble outCArray [] = {sum,average}; //步骤3:将C的Native jdouble []转换为JNI jdoublearray,并返回 jdoubleArray outJNIArray =(* env) - > NewDoubleArray(env,2); //分配 if(NULL == outJNIArray)返回NULL; (* env) - > SetDoubleArrayRegion(env,outJNIArray,0,2,outCArray); //复制 退出JJIArray; } |
JNI原始数组函数
所述JNI原始阵列(jintArray
,jbyteArray
,jshortArray
,jlongArray
,jfloatArray
,jdoubleArray
,jcharArray
和jbooleanArray
)的功能是:
<span style="color:#000000"><span style="color:#009900">// <em>ArrayType</em>:jintArray,jbyteArray,jshortArray,jlongArray,jfloatArray,jdoubleArray,jcharArray,jbooleanArray
// <em>PrimitiveType</em>:int,byte,short,long,float,double,char,boolean
// <em>NativeType</em>:jint,jbyte,jshort,jlong,jfloat,jdouble,jchar,jboolean </span>
<em>NativeType</em> * <strong>Get < <em>PrimitiveType</em> > ArrayElements</strong>(JNIEnv * env,<em>ArrayType</em> array,jboolean * isCopy);
void <strong>Release < <em>PrimitiveType</em> > ArrayElements</strong>(JNIEnv * env,<em>ArrayType</em>数组,<em>NativeType</em> * elems,jint模式);
void <strong>Get < <em>PrimitiveType</em> > ArrayRegion</strong>(JNIEnv * env,<em>ArrayType</em>数组,jsize start,jsize length,<em>NativeType</em> * buffer);
void <strong>Set < <em>PrimitiveType</em> > ArrayRegion</strong>(JNIEnv * env,<em>ArrayType</em>数组,jsize start,jsize length,const <em>NativeType</em> * buffer);
<em>ArrayType </em> <strong>New < <em>PrimitiveType</em> > Array</strong>(JNIEnv * env,jsize length);
void * <strong>GetPrimitiveArrayCritical</strong>(JNIEnv * env,jarray数组,jboolean * isCopy);
void <strong>ReleasePrimitiveArrayCritical</strong>(JNIEnv * env,jarray数组,void * carray,jint模式);</span>
该可用于创建一个新的C的本地数组从给定的Java 。可用于复制(或从部分的),并从一预先分配的 C本机阵列。GET|Release< PrimitiveType >ArrayElements()
jxxx[]
jxxxArray
GET|Set<PrimitiveType >ArrayRegion()
jxxxArray
start
length
jxxx[]
该New<PrimitiveType>Array()
可用于分配一个新的jxxxArray
给定尺寸的。然后,您可以使用该函数从本机数组中填充其内容。Set< PrimitiveType >ArrayRegion()
jxxx[]
这些Get|ReleasePrimitiveArrayCritical()
函数不允许在get和release之间阻塞调用。
上一篇: 逆转知识第十课:汇编语言中循环的呈现方式及代码恢复
下一篇: 玩转汇编语言:轻松理解冒泡排序算法