欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

JNI基础知识:原型数据——如何传递字符串(Java编程教程第4.2节)

最编程 2024-02-13 13:45:58
...

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*访问)提供转换功能:

  1. 要从JNI字符串( jstring )获取C字符串( char* ),请调用方法const char* GetStringUTFChars(JNIEnv*, jstring, jboolean*) 。
  2. 要从C字符串( char* )获取JNI字符串( jstring ),请调用方法jstring NewStringUTF(JNIEnv*, char*) 。

C实现TestJNIString.c如下。

  1. 它接收JNI字符串( jstring ),通过GetStringUTFChars()转换为C字符串( char* GetStringUTFChars() 。
  2. 然后它执行其预期的操作 - 显示收到的字符串并提示用户返回另一个字符串。
  3. 它通过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

 1
 2
 3
 4
五
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17 
 2
 3
 4
五
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17 
公共类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原始阵列,即jintArrayjbyteArrayjshortArrayjlongArrayjfloatArrayjdoubleArrayjcharArrayjbooleanArray用于Java的原始阵列intbyteshortlongfloatdoublecharboolean分别。它还定义了一个jobjectArrayfor Java的数组Object(稍后讨论)。

同样,你需要JNI阵列和本地阵列,例如之间的转换,之间jintArray和C的jint[],或jdoubleArray和C的jdouble[]。JNI Environment接口为转换提供了一组函数:

  1. 要从jint[]JNI 获取C本机jintArray,请调用jint* GetIntArrayElements(JNIEnv *env, jintArray a, jboolean *iscopy)
  2. 要从jintArrayC本机获取JNI jint[],首先,调用jintArray NewIntArray(JNIEnv *env, jsize len)分配,然后使用void SetIntArrayRegion(JNIEnv *env, jintArray a, jsize start, jsize len, const jint *buf)从中复制jint[]jintArray

上面有8组函数,一组用于八个Java原语中的每一个。

本机程序需要:

  1. 接收传入的JNI阵列(例如jintArray),转换为C的本机阵列(例如jint[])。
  2. 执行其预期的操作。
  3. 将返回C的本机数组(例如jdouble[])转换为JNI数组(例如jdoubleArray),并返回JNI数组。

C实现“ TestJNIPrimitiveArray.c”是:

 1
 2
 3
 4
五
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28 
 2
 3
 4
五
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28 
#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原始阵列(jintArrayjbyteArrayjshortArrayjlongArrayjfloatArrayjdoubleArrayjcharArrayjbooleanArray)的功能是:

<span style="color:#000000"><span style="color:#009900">// <em>ArrayType</em>:jintArray,jbyteArray,jshortArray,jlong​​Array,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[]jxxxArrayGET|Set<PrimitiveType >ArrayRegion()jxxxArraystartlengthjxxx[]

New<PrimitiveType>Array()可用于分配一个新的jxxxArray给定尺寸的。然后,您可以使用该函数从本机数组中填充其内容。Set< PrimitiveType >ArrayRegion()jxxx[]

这些Get|ReleasePrimitiveArrayCritical()函数不允许在get和release之间阻塞调用。